diff --git a/build/404.html b/build/404.html index 1aab474c6..078598da2 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 f718c1361..4699fecf1 100644 --- a/build/Tutorials/Components/Core/BoundingBoxer/index.html +++ b/build/Tutorials/Components/Core/BoundingBoxer/index.html @@ -4,15 +4,15 @@ BoundingBoxer | That Open Docs - - + +
-
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. +

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!

- - +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 new file mode 100644 index 000000000..b58057ff4 --- /dev/null +++ b/build/Tutorials/Components/Core/Classifier/index.html @@ -0,0 +1,16 @@ + + + + + +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 f2be35b03..32436cd85 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 label="Double click: Create clipping plane"></bim-label>
<bim-label 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.

- - +
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 label="Double click: Create clipping plane"></bim-label>
<bim-label 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 aed58cd22..7cc90373c 100644 --- a/build/Tutorials/Components/Core/Cullers/index.html +++ b/build/Tutorials/Components/Core/Cullers/index.html @@ -4,15 +4,15 @@ Cullers | That Open Docs - - + +
-
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 "../..";

🌎 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. +

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 "../..";

🌎 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.

- - +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 new file mode 100644 index 000000000..6ef62940b --- /dev/null +++ b/build/Tutorials/Components/Core/Exploder/index.html @@ -0,0 +1,16 @@ + + + + + +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 1474e6eaa..be0503bfa 100644 --- a/build/Tutorials/Components/Core/FragmentsManager/index.html +++ b/build/Tutorials/Components/Core/FragmentsManager/index.html @@ -4,29 +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.

🧶 Managing Fragment Efficiently


Until recently, we had been adding 3D objects to the Scene with the traditional scene.add API. -We will now use Fragment to work with large BIM models.🏗️ -Fragment are faster and easier to use Geometric API, which reduces draw calls and speeds up the processing of 3D objects. -In this tutorial, we will load and render .frag files using Fragment Manager.

First, let's set up a simple scene!

👀 If you haven't started there, check out that tutorial first!

🧭 Fragment Powerhouse


Let's begin by including Fragment Manager, -which requires the parameter component to be provided to it. -In terms of capabilities, Fragment Manager is a true powerhouse.🏭

const fragments = new OBC.FragmentsManager(components);

🧩 Add Fragment To Scene


Using a single API, a Fragment can be quickly added to the scene. -Everything else is taken care of by fragment.load, which makes it easier to render a Fragment file.💪💪

let uuid: string = "";

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;
console.log(group);
}
Loading IFC files as Fragment

You're probably wondering how IFC files can be loaded as Fragment. -The same approach can be used to load an IFC file with a fragment; -view its own tutorial for further information.

📤 Storing Fragment


Fragment Manager provides us with option to export the Fragment we have used in our Scene. -Fragment are exported to Blob, -which can be used to generate a File and then download it.↗️ -Let's see how you can use fragment.export in your code.

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();
}

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);
}
Creating Dynamic URLs

URL.createObjectURL() comes handy when you want to generate a URL for files that were generated programmatically. -You can read more about it here.

🧹 Discard Fragment and Clean the Scene


At some point, you will require to render numerous Fragment and discard them when not needed. -You can use dispose() method which will remove the Fragment Meshes from the Scene.✂️ -After using fragments.dispose(), you should reinitialize Fragment Manager to maintain it's original state for further uses.

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

Loading a .zip fragment that you have locally is also quite easy:

function importExternalFragment() {
if (fragments.groups.size) {
return;
}
const input = document.createElement("input");
input.type = "file";
input.onchange = async () => {
if (!(input.files && input.files[0])) return;
const file = input.files[0];
if (file.name.includes(".frag")) {
const url = URL.createObjectURL(file);
const result = await fetch(url);
const data = await result.arrayBuffer();
const buffer = new Uint8Array(data);
fragments.load(buffer);
}
input.remove();
};
input.click();
}

const gui = new dat.GUI();

const actions = {
loadFragments: () => loadFragments(),
disposeFragments: () => disposeFragments(),
exportFragments: () => exportFragments(),
importExternalFragment: () => importExternalFragment(),
};

gui.add(actions, "loadFragments").name("Load Fragments");
gui.add(actions, "disposeFragments").name("Dispose Fragments");
gui.add(actions, "exportFragments").name("Export Fragments");
gui.add(actions, "importExternalFragment").name("Import External Fragment");

Congratulations 🎉 on completing this short yet powerful tutorial! -Now you can render or export Fragment files in your BIM Apps using Fragment Manager 🎯 -Let's keep it up and check out another tutorial! 🎓

- - +
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 52beb2f2c..46c83cf3d 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!

- - +
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 838ed711a..1b32008c4 100644 --- a/build/Tutorials/Components/Core/Hider/index.html +++ b/build/Tutorials/Components/Core/Hider/index.html @@ -4,35 +4,18 @@ Hider | That Open Docs - - + +
-
Skip to main content

Hider

Source

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

🔎 Custom filters for your BIM models


BIM models are complex, and finding what we are looking for is not -always easy. Luckily, the components library has tools to make -it easier, and one of them is the 'FragmentHider'. Let's -check it out!

Complex IFC, complex filters

Each IFC is a world. Data is always defined slightly differently, -and defining pre-made filters only works for very basic things -like categories. With the FragmentHider, you'll be able to find -anything, even things defined in custom categories!

First, let's start by creating a FragmentManager instance and +

Hider

Source

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

👓 Making things invisible


In this tutorial, you'll learn how control the visibility of the items of a BIM model.

Why make things invisible?

Many times, we just want to look at a specific part of a BIM model, without seeing the rest of it. BIM models are complex, and finding what we are looking for is not always easy. Luckily, the components library has tools to make it easier!

In this tutorial, we will import:

  • @thatopen/ui to add some simple and cool UI menus.
  • 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;

🔎 Custom filters for your BIM models


First, let's start by creating a FragmentManager instance and loading a simple fragment. If you haven't checked out the tutorial -for that component yet, do it before going forward!

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);

Now that we have our model, let's start the FragmentHider. You +for that component yet, do it before going forward!

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 properties = await fetch(
"https://thatopen.github.io/engine_components/resources/small.json",
);
model.setLocalProperties(await properties.json());

const indexer = components.get(OBC.IfcRelationsIndexer);
const relationsFile = await fetch(
"https://thatopen.github.io/engine_components/resources/small-relations.json",
);
const relations = indexer.getRelationsMapFromJSON(await relationsFile.text());
indexer.setRelationMap(model, relations);

Now that we have our model, let's start the FragmentHider. You 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 = new OBC.FragmentHider(components);

📕📗📘 Setting up simple filters


Next, we will classify data by category and by level using the -FragmentClassifier. This will allow us to create a simple -filter for both classifications. Don't worry: we'll get to -the more complex filters later!

const classifier = new OBC.Classifier(components);
classifier.byStorey(model);
classifier.byEntity(model);

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

const storeys: Record<string, any> = {};
const storeyNames = Object.keys(classifier.list.storeys);
for (const name of storeyNames) {
storeys[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;
}

Finally, we will set up a simple menu to control -the visibility of storeys:

BUI.Manager.init();

const panel = BUI.Component.create<BUI.PanelSection>(() => {
return BUI.html`
<bim-panel active label="Hider Tutorial"
style="position: fixed; top: 5px; right: 5px">

<bim-panel-section fixed name="Floors" style="padding-top: 10px;">
</bim-panel-section>

<bim-panel-section fixed name="Categories" style="padding-top: 10px;">
</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 storeys) {
const panel = BUI.Component.create<BUI.Checkbox>(() => {
return BUI.html`
<bim-checkbox checked label="${name}"
@change="${({ target }: { target: BUI.Checkbox }) => {
const found = classifier.find({ storeys: [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);
}

That's it! That button will open a floating menu that will allow -you to create custom multi-filters that work even for custom -property sets and quantity sets, including logical operators. -Try them out in the example below, and check out more tutorials -to bring your BIM apps to the next level!

- - +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 d2237a010..d8f1c90bc 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.

💪 Let's go BIG


Do you need to open huge big IFC files fast, even on more modest devices? If so, you are in luck! We can open virtually any model on any device in seconds thanks to BIM TILES!

BIM tiles?

The idea behind BIM tiles is pretty simple! Instead of loading the whole BIM model at once, we just load the explicit geometries that are seen by the user. It's way faster than opening the IFC directly, but for this you'll need a backend (or to rely on the file system of the user if you are building a desktop or mobile app).

Let's see how to do this step by step!

🧩 Converting the IFC model to tiles


The first step is to transform the IFC model into BIM tiles. The reason why we have to do this is pretty simple: geometry in IFC is implicit (e.g. a wall is defined as an extrusion). This means that it needs to be computed and converted to explicit geometry (triangles) so that it can be displayed in 3D.

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!

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, let's start converting the IFC geometry to tiles and getting those files so the streamer can do its job. In order to do it, we need the first component from the collection of streaming components: FragmentIfcStreamConverter:

// We need this wasm configuration later to convert properties
const wasm = {
path: "https://unpkg.com/web-ifc@0.0.53/",
absolute: true,
};

const tiler = new OBC.IfcGeometryTiler(components);
tiler.settings.wasm = wasm;
tiler.settings.minGeometrySize = 20;
tiler.settings.minAssetsSize = 1000;

The FragmentIfcStreamConverter class 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:

// @ts-ignore
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";
}

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. +

IfcGeometryTiler

Source

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

💪 Let's go BIG


Do you need to open huge big IFC files fast, even on more modest devices? If so, you are in luck! We can open virtually any model on any device in seconds thanks to BIM TILES!

BIM tiles?

The idea behind BIM tiles is pretty simple! Instead of loading the whole BIM model at once, we just load the explicit geometries that are seen by the user. It's way faster than opening the IFC directly, but for this you'll need a backend (or to rely on the file system of the user if you are building a desktop or mobile app).

Let's see how to do this step by step!

🧩 Converting the IFC model to tiles


The first step is to transform the IFC model into BIM tiles. The reason why we have to do this is pretty simple: geometry in IFC is implicit (e.g. a wall is defined as an extrusion). This means that it needs to be computed and converted to explicit geometry (triangles) so that it can be displayed in 3D.

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!

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, let's start converting the IFC geometry to tiles and getting those files so the streamer can do its job. In order to do it, we need the first component from the collection of streaming components: FragmentIfcStreamConverter:

// We need this wasm configuration later to convert properties
const wasm = {
path: "https://unpkg.com/web-ifc@0.0.53/",
absolute: true,
};

const tiler = new OBC.IfcGeometryTiler(components);
tiler.settings.wasm = wasm;
tiler.settings.minGeometrySize = 20;
tiler.settings.minAssetsSize = 1000;

The FragmentIfcStreamConverter class 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:

// @ts-ignore
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";
}

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, the FragmentIfcStreamConverter as well as other components in the collection of streaming 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 That Open Engine?

If you're familiar with That Open Engine, 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!

This is pretty much it! 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;
});
});

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 💪

- - + + \ 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 945e2a32d..49af6b989 100644 --- a/build/Tutorials/Components/Core/IfcLoader/index.html +++ b/build/Tutorials/Components/Core/IfcLoader/index.html @@ -4,12 +4,12 @@ IfcLoader | That Open Docs - - + +
-

IfcLoader

Source

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

🏠👉🤖 From IFC to fragment in 1 minute


Fragments are great: they are lightweight, they are fast and we +

IfcLoader

Source

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

🏠👉🤖 From IFC to fragment in 1 minute


Fragments 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? Easy: with the FragmentIfcLoader! Let's start by creating it.

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 @@ -47,7 +47,7 @@ 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 💪

- - + + \ 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 4a0ade715..7d77612bf 100644 --- a/build/Tutorials/Components/Core/IfcPropertiesTiler/index.html +++ b/build/Tutorials/Components/Core/IfcPropertiesTiler/index.html @@ -4,12 +4,12 @@ IfcPropertiesTiler | That Open Docs - - + +
-

IfcPropertiesTiler

Source

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

📋 Streaming the properties


You can also stream the properties of an IFC file. Why? Because some files can have +

IfcPropertiesTiler

Source

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

📋 Streaming the properties


You can also stream the properties of an IFC file. Why? Because some files can have millions of properties, and trying to save them naively in a normal DB is not very scalable/affordable. Using this system, you'll be able to store and retrieve the data of models of any size without big cloud costs. We can do this conversion @@ -17,7 +17,7 @@ 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 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);
});

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 💪

- - + + \ 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 a5bed7b20..fa986fa9e 100644 --- a/build/Tutorials/Components/Core/IfcRelationsIndexer/index.html +++ b/build/Tutorials/Components/Core/IfcRelationsIndexer/index.html @@ -4,14 +4,14 @@ IfcRelationsIndexer | That Open Docs - - + +
-

IfcRelationsIndexer

Source

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

Getting entity relations the easy way 💪


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. +

IfcRelationsIndexer

Source

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

Getting entity relations the easy way 💪


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. Now, that 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! 🔥🔥

Loading a model 🏦

First things first, lets load an IFC model to process its relations.

tip

If you're unsure on the details to load a model, just tool at the IfcLoader tutorial!

const ifcLoader = components.get(OBC.IfcLoader);
await ifcLoader.setup();
const file = await fetch("/resources/small.ifc");
const buffer = await file.arrayBuffer();
const typedArray = new Uint8Array(buffer);
const model = await ifcLoader.load(typedArray);
world.scene.three.add(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 IfcRelationsIndexer 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. The important thing is that 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.

Congratulations! 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 f0f16593b..8ff4141c5 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 - - + +
-

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 floorplans?

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-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 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.

- - +

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 59135303d..7c91420d9 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 - - + +
-

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

const 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.

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

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

const grids = components.get(OBC.Grids);
const 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-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!

- - +

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 a13afb239..c08f952ab 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 - - + +
-

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.

- - +

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/Worlds/index.html b/build/Tutorials/Components/Core/Worlds/index.html index 77ba3cdc1..37fc1ea62 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 - - + +
-

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.

- - +

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 0b316b47d..788348478 100644 --- a/build/Tutorials/Components/Front/AngleMeasurement/index.html +++ b/build/Tutorials/Components/Front/AngleMeasurement/index.html @@ -4,8 +4,8 @@ AngleMeasurement | That Open Docs - - + +
@@ -30,7 +30,7 @@ dimensions.deleteAll()

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

// const mainToolbar = new OBC.Toolbar(components, {
// name: "Main Toolbar",
// position: "bottom",
// });
// mainToolbar.addChild(dimensions.uiElement.get("main"));
// components.ui.addToolbar(mainToolbar);

🎛️ Check Toolbar and UIManager tutorial if you have any doubts!

🖌️ Adding Styles


Few final things, we need to add styles for the labels which display the measurement information.

  • ifcjs-dimension-label - The label which is used to show the metric value after both the tooltips are attached.
  • ifcjs-dimension-label:hover - Changing the styling when someone hovers on the dimension label.
  • ifcjs-dimension-preview - The label which shows the measurement when the tooltip is not yet attached.
style
.ifcjs-dimension-label {
background-color: black;
font-family: sans-serif;
color: white;
padding: 8px;
border-radius: 8px;
pointer-events: all;
transition: background-color 200ms ease-in-out;
}
.ifcjs-dimension-label:hover {
background-color: grey;
}
.ifcjs-dimension-preview {
background-color: #ffffff;
width: 2rem;
height: 2rem;
opacity: 0.3;
padding: 8px;
border-radius: 100%;
}

Congratulations 🎉 on completing this tutorial! Now you can measure any BIM Model or any 3D Object easily using Simple Dimension Component 📐 Let's keep it up and check out another tutorial! 🎓

- - + + \ 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 1d0a0b048..407e06788 100644 --- a/build/Tutorials/Components/Front/AreaMeasurement/index.html +++ b/build/Tutorials/Components/Front/AreaMeasurement/index.html @@ -4,8 +4,8 @@ AreaMeasurement | That Open Docs - - + +
@@ -30,7 +30,7 @@ dimensions.deleteAll()

window.onkeydown = (event) => {
if (event.code === "Delete" || event.code === "Backspace") {
// WORK IN PROGRESS
// dimensions.delete();
}
};

🎛️ Check Toolbar and UIManager tutorial if you have any doubts!

🖌️ Adding Styles


Few final things, we need to add styles for the labels which display the measurement information.

  • ifcjs-dimension-label - The label which is used to show the metric value after both the tooltips are attached.
  • ifcjs-dimension-label:hover - Changing the styling when someone hovers on the dimension label.
  • ifcjs-dimension-preview - The label which shows the measurement when the tooltip is not yet attached.
style
.ifcjs-dimension-label {
background-color: black;
font-family: sans-serif;
color: white;
padding: 8px;
border-radius: 8px;
pointer-events: all;
transition: background-color 200ms ease-in-out;
}
.ifcjs-dimension-label:hover {
background-color: grey;
}
.ifcjs-dimension-preview {
background-color: #ffffff;
width: 2rem;
height: 2rem;
opacity: 0.3;
padding: 8px;
border-radius: 100%;
}

Congratulations 🎉 on completing this tutorial! Now you can measure any BIM Model or any 3D Object easily using Simple Dimension Component 📐 Let's keep it up and check out another tutorial! 🎓

- - + + \ 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 e6bbe23de..5b3f16768 100644 --- a/build/Tutorials/Components/Front/EdgesClipper/index.html +++ b/build/Tutorials/Components/Front/EdgesClipper/index.html @@ -4,8 +4,8 @@ EdgesClipper | That Open Docs - - + +
@@ -29,7 +29,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! 🎓

- - + + \ 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 ba4d0ee2d..035e6a086 100644 --- a/build/Tutorials/Components/Front/IfcStreamer/index.html +++ b/build/Tutorials/Components/Front/IfcStreamer/index.html @@ -4,8 +4,8 @@ IfcStreamer | That Open Docs - - + +
@@ -19,7 +19,7 @@ and clear the cache using the clearCache() method:

loader.useCache = true;

async function clearCache() {
await loader.clearCache();
window.location.reload();
}

You can also customize the loader through the culler property:

loader.culler.threshold = 10;
loader.culler.maxHiddenTime = 1000;
loader.culler.maxLostTime = 40000;

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 7bd2b1696..6dd167db3 100644 --- a/build/Tutorials/Components/Front/LengthMeasurement/index.html +++ b/build/Tutorials/Components/Front/LengthMeasurement/index.html @@ -4,8 +4,8 @@ LengthMeasurement | That Open Docs - - + +
@@ -31,7 +31,7 @@ Dimensions can be removed using dimensions.delete(). dimensions.delete() deletes the dimension on which your mouse pointer is now located.

Deleting all the Dimensions

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

- - + + \ 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 fa5d2780b..e3cb98b42 100644 --- a/build/Tutorials/Components/Front/PostproductionRenderer/index.html +++ b/build/Tutorials/Components/Front/PostproductionRenderer/index.html @@ -4,8 +4,8 @@ PostproductionRenderer | That Open Docs - - + +
@@ -16,7 +16,7 @@ Also, we will enable the visibility for post-production layer.

const { postproduction } = world.renderer;
postproduction.enabled = true;
postproduction.customEffects.excludedMeshes.push(grid.three);

const ao = postproduction.n8ao.configuration;

BUI.Manager.init();

const panel = BUI.Component.create<BUI.PanelSection>(() => {
return BUI.html`
<bim-panel label="Clipper 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);

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);

Congratulations 🎉 on completing this tutorial! Now you know how to add cool effects easily using Post Production 🖼️ Let's keep it up and check out another tutorial! 🎓

- - + + \ 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 b64e15a39..e094061bb 100644 --- a/build/Tutorials/Components/Front/ShadowDropper/index.html +++ b/build/Tutorials/Components/Front/ShadowDropper/index.html @@ -4,8 +4,8 @@ ShadowDropper | That Open Docs - - + +
@@ -26,7 +26,7 @@ Now you can add shadows to BIM Models or any 3D Object in minutes using Shadow Dropper 🌗 Let's keep it up and check out another tutorial! 🎓

- - + + \ No newline at end of file diff --git a/build/Tutorials/Components/index.html b/build/Tutorials/Components/index.html index 5dd594366..009a40a33 100644 --- a/build/Tutorials/Components/index.html +++ b/build/Tutorials/Components/index.html @@ -4,15 +4,15 @@ Components | That Open Docs - - + +
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);
- - +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 f0ad3f3d4..5f1fd68b5 100644 --- a/build/Tutorials/UserInterface/Core/Component/index.html +++ b/build/Tutorials/UserInterface/Core/Component/index.html @@ -4,16 +4,16 @@ Component | That Open Docs - - + +
Skip to main content

Component

Source

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

Leveling up your app with custom components! 🔌


One of the greatest things about the library is that you can create your own reactive and non reactive elements (statefull and stateless components respectively) in a very simple and efficient way, all thanks to the power of lit-html 💪. The UIComponent class has a static method to create functional components (UI defined as a function) that can be updated anytime. The method is UIComponent.create.

note

Despite the UIComponent 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 UIManager.init() before anything else!

UIComponent.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 literals 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 data 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 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, UIComponent.create must have a second argument to specify the initial state of the component.
  3. Now, UIComponent.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-components-obc or @thatopen/ui-components-three 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 😉

- - +The component we just created is stateless, because it doesn't have any state in which its data 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, UIComponent.create must have a second argument to specify the initial state of the component.
  3. Now, UIComponent.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-components-obc or @thatopen/ui-components-three 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 c6d3eb63f..ec03dd331 100644 --- a/build/Tutorials/UserInterface/OBC/ClassificationsTree/index.html +++ b/build/Tutorials/UserInterface/OBC/ClassificationsTree/index.html @@ -4,15 +4,15 @@ ClassificationsTree | That Open Docs - - + +
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", "predefinedTypes"],
"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 4e1593020..d9b789a8f 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("/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"></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! 💪

- - +
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"></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 04e001394..e88aa192f 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("/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.TableDefinition = {
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 label=${entity} style=${BUI.styleMap(style)}></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 label=${type} style=${BUI.styleMap(style)}></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 label=${value} style=${BUI.styleMap(style)}></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);
- - +
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 2bf55a10e..d6c446f20 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 497fd8eae..24b772551 100644 --- a/build/Tutorials/UserInterface/index.html +++ b/build/Tutorials/UserInterface/index.html @@ -4,8 +4,8 @@ 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 6c07762b2..ca1ff4793 100644 --- a/build/api/classes/thatopen_components.AsyncEvent/index.html +++ b/build/api/classes/thatopen_components.AsyncEvent/index.html @@ -4,8 +4,8 @@ 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 cf1b5df81..c47b7b6fe 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 f8bc96dc3..2e7a1def5 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 da27c4425..79ac85927 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 b91d852d5..f43e01162 100644 --- a/build/api/classes/thatopen_components.Clipper/index.html +++ b/build/api/classes/thatopen_components.Clipper/index.html @@ -4,8 +4,8 @@ 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 853bc3972..28c422016 100644 --- a/build/api/classes/thatopen_components.Component/index.html +++ b/build/api/classes/thatopen_components.Component/index.html @@ -4,8 +4,8 @@ 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 ff0d60fa4..e2608193f 100644 --- a/build/api/classes/thatopen_components.Components/index.html +++ b/build/api/classes/thatopen_components.Components/index.html @@ -4,8 +4,8 @@ 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 ffcdf30d8..d179c9c37 100644 --- a/build/api/classes/thatopen_components.CullerRenderer/index.html +++ b/build/api/classes/thatopen_components.CullerRenderer/index.html @@ -4,8 +4,8 @@ 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 5bf81928e..fcc60d953 100644 --- a/build/api/classes/thatopen_components.Cullers/index.html +++ b/build/api/classes/thatopen_components.Cullers/index.html @@ -4,8 +4,8 @@ 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 517f1a040..d4a3d3f2f 100644 --- a/build/api/classes/thatopen_components.Disposer/index.html +++ b/build/api/classes/thatopen_components.Disposer/index.html @@ -4,8 +4,8 @@ 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 09d58c8b1..2c121bb8b 100644 --- a/build/api/classes/thatopen_components.Event/index.html +++ b/build/api/classes/thatopen_components.Event/index.html @@ -4,8 +4,8 @@ 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 2ee7e9fef..f9290f450 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 dbaccbb2a..d24c957e0 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:129


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 ; 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 a7db09cae..fa31ec2ac 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 3de72eab8..f28c61300 100644 --- a/build/api/classes/thatopen_components.IfcRelationsIndexer/index.html +++ b/build/api/classes/thatopen_components.IfcRelationsIndexer/index.html @@ -4,8 +4,8 @@ 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 50106e8eb..f39ffd038 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 efcbab30c..9faaa7a7f 100644 --- a/build/api/classes/thatopen_components.MeshCullerRenderer/index.html +++ b/build/api/classes/thatopen_components.MeshCullerRenderer/index.html @@ -4,8 +4,8 @@ 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 a8c361e4d..f4218d045 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 564c0c14a..680242a7a 100644 --- a/build/api/classes/thatopen_components.OrthoPerspectiveCamera/index.html +++ b/build/api/classes/thatopen_components.OrthoPerspectiveCamera/index.html @@ -4,8 +4,8 @@ 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:80


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:108


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:90


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:142


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:105


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:117

- - + + \ 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 5bd9c591a..973be94ca 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 b478adb42..5807665fa 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 d7998017f..d116396f6 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 ded79f563..02c477acc 100644 --- a/build/api/classes/thatopen_components.SimpleCamera/index.html +++ b/build/api/classes/thatopen_components.SimpleCamera/index.html @@ -4,8 +4,8 @@ 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:105


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:117

- - + + \ 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 d70602c60..502021a43 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 703a794c0..c80a565b8 100644 --- a/build/api/classes/thatopen_components.SimpleRenderer/index.html +++ b/build/api/classes/thatopen_components.SimpleRenderer/index.html @@ -4,8 +4,8 @@ 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 7f0f90d20..a865f3522 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 9491d32a7..f129d1c46 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 db77a965e..bb0cd7434 100644 --- a/build/api/classes/thatopen_components_front.EdgesPlane/index.html +++ b/build/api/classes/thatopen_components_front.EdgesPlane/index.html @@ -4,15 +4,15 @@ Class: EdgesPlane | That Open Docs - - + +
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 554a40f79..d663472bd 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 2ae58b72a..b0e0671d4 100644 --- a/build/api/classes/thatopen_components_front.Marker/index.html +++ b/build/api/classes/thatopen_components_front.Marker/index.html @@ -4,15 +4,15 @@ Class: Marker | That Open Docs - - + +
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 6fcd8e7ec..7e76edb89 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 769aed3f0..b9f6cd71f 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 a1c46335a..734460096 100644 --- a/build/api/classes/thatopen_components_front.RendererWith2D/index.html +++ b/build/api/classes/thatopen_components_front.RendererWith2D/index.html @@ -4,8 +4,8 @@ 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 b443598fa..7a8082f3f 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 d6bb169c7..b9b2e0a77 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 f1cee1f34..4830f64cb 100644 --- a/build/api/classes/thatopen_ui.Checkbox/index.html +++ b/build/api/classes/thatopen_ui.Checkbox/index.html @@ -4,15 +4,15 @@ Class: Checkbox | That Open Docs - - + +
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 4710b0582..f6d19a0b0 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 10ed0de82..92b34e21d 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 3d6f6d3f7..97b18d7a8 100644 --- a/build/api/classes/thatopen_ui.ContextMenu/index.html +++ b/build/api/classes/thatopen_ui.ContextMenu/index.html @@ -4,8 +4,8 @@ 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 63eeea909..8478f092b 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 3a0449e96..a43a1596b 100644 --- a/build/api/classes/thatopen_ui.Grid/index.html +++ b/build/api/classes/thatopen_ui.Grid/index.html @@ -4,8 +4,8 @@ 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 f1be027aa..03dcda99f 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 1ceb693d6..a18a5ffb5 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 6d34d5611..98264541c 100644 --- a/build/api/classes/thatopen_ui.Label/index.html +++ b/build/api/classes/thatopen_ui.Label/index.html @@ -4,8 +4,8 @@ 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 6c6f1c166..23fd692ca 100644 --- a/build/api/classes/thatopen_ui.NumberInput/index.html +++ b/build/api/classes/thatopen_ui.NumberInput/index.html @@ -4,8 +4,8 @@ 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 7ff4a921d..d943c38f1 100644 --- a/build/api/classes/thatopen_ui.Option/index.html +++ b/build/api/classes/thatopen_ui.Option/index.html @@ -4,8 +4,8 @@ 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 8e8ec936e..89c3b70d0 100644 --- a/build/api/classes/thatopen_ui.Panel/index.html +++ b/build/api/classes/thatopen_ui.Panel/index.html @@ -4,8 +4,8 @@ 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 c9cc7f086..f936c46a6 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 808307944..31d969ee8 100644 --- a/build/api/classes/thatopen_ui.Selector/index.html +++ b/build/api/classes/thatopen_ui.Selector/index.html @@ -4,15 +4,15 @@ Class: Selector | That Open Docs - - + +
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 638ac0cdd..7b643e01b 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 7fa9aca25..b1ed975aa 100644 --- a/build/api/classes/thatopen_ui.Table/index.html +++ b/build/api/classes/thatopen_ui.Table/index.html @@ -4,8 +4,8 @@ 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 cb33417d9..bd080d2af 100644 --- a/build/api/classes/thatopen_ui.Tabs/index.html +++ b/build/api/classes/thatopen_ui.Tabs/index.html @@ -4,8 +4,8 @@ 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 dbc3ec106..09f5438a7 100644 --- a/build/api/classes/thatopen_ui.TextInput/index.html +++ b/build/api/classes/thatopen_ui.TextInput/index.html @@ -4,8 +4,8 @@ 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 64fb16ae8..9ff09a423 100644 --- a/build/api/classes/thatopen_ui.Toolbar/index.html +++ b/build/api/classes/thatopen_ui.Toolbar/index.html @@ -4,8 +4,8 @@ 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 e223aaee6..e60c8b39a 100644 --- a/build/api/classes/thatopen_ui.ToolbarGroup/index.html +++ b/build/api/classes/thatopen_ui.ToolbarGroup/index.html @@ -4,15 +4,15 @@ Class: ToolbarGroup | That Open Docs - - + +
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 4b88b0fe2..41e280e36 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 ffe76676d..283916a89 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 2eb24560d..fa79dac4d 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 0965cf5b9..cb948b89a 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 ba3127c37..aa3c53e9e 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 2a6be897d..3f92953db 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 1705b0314..1c1b0a056 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 e68ff2efb..8990c1217 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 524ed0457..172ee93de 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 534599cc4..782aa5f38 100644 --- a/build/api/interfaces/thatopen_components.Createable/index.html +++ b/build/api/interfaces/thatopen_components.Createable/index.html @@ -4,8 +4,8 @@ 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 6ba293b4f..1b59e4f1f 100644 --- a/build/api/interfaces/thatopen_components.Disposable/index.html +++ b/build/api/interfaces/thatopen_components.Disposable/index.html @@ -4,8 +4,8 @@ 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 d854a256c..38f90515f 100644 --- a/build/api/interfaces/thatopen_components.Hideable/index.html +++ b/build/api/interfaces/thatopen_components.Hideable/index.html @@ -4,8 +4,8 @@ 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 363267708..6ca0b7a46 100644 --- a/build/api/interfaces/thatopen_components.NavigationMode/index.html +++ b/build/api/interfaces/thatopen_components.NavigationMode/index.html @@ -4,8 +4,8 @@ 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 85eacc1b8..ec67b87ad 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 16017d706..98a0327e2 100644 --- a/build/api/interfaces/thatopen_components.Resizeable/index.html +++ b/build/api/interfaces/thatopen_components.Resizeable/index.html @@ -4,8 +4,8 @@ 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 90485e579..e3e1007f2 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 fbbb0b764..4e7f24143 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 88d0d9418..3ecb32f41 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 dc9c27c02..74e78d0f9 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 422a3560e..551b36ff8 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 066556e7d..da35e8e8e 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 78007b75e..63ccc1362 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 c82f2d579..7427c15af 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 8f8af830b..2c3c99a23 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 184146ba6..8b130fe92 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 c4c2813b8..7d8781a09 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 a0377219b..0b5a4845c 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/035e0bbb.1bdb4d2e.js b/build/assets/js/035e0bbb.1bdb4d2e.js new file mode 100644 index 000000000..50ebf58e3 --- /dev/null +++ b/build/assets/js/035e0bbb.1bdb4d2e.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkdocs=self.webpackChunkdocs||[]).push([[5269],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>h});var o=n(7294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(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||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(o=0;o=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var s=o.createContext({}),c=function(e){var t=o.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},p=function(e){var t=c(e.components);return o.createElement(s.Provider,{value:t},e.children)},m="mdxType",u={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},d=o.forwardRef((function(e,t){var n=e.components,r=e.mdxType,a=e.originalType,s=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),m=c(n),d=r,h=m["".concat(s,".").concat(d)]||m[d]||u[d]||a;return n?o.createElement(h,i(i({ref:t},p),{},{components:n})):o.createElement(h,i({ref:t},p))}));function h(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var a=n.length,i=new Array(a);i[0]=d;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l[m]="string"==typeof e?e:r,i[1]=l;for(var c=2;c{n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>s,default:()=>h,frontMatter:()=>l,metadata:()=>c,toc:()=>m});var o=n(7462),r=n(3366),a=(n(7294),n(3905)),i=["components"],l={},s=void 0,c={unversionedId:"Tutorials/Components/Core/OrthoPerspectiveCamera",id:"Tutorials/Components/Core/OrthoPerspectiveCamera",title:"OrthoPerspectiveCamera",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/OrthoPerspectiveCamera.mdx",sourceDirName:"Tutorials/Components/Core",slug:"/Tutorials/Components/Core/OrthoPerspectiveCamera",permalink:"/Tutorials/Components/Core/OrthoPerspectiveCamera",draft:!1,tags:[],version:"current",frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"MiniMap",permalink:"/Tutorials/Components/Core/MiniMap"},next:{title:"Raycasters",permalink:"/Tutorials/Components/Core/Raycasters"}},p={},m=[{value:"\ud83d\udcf9 How to handle a fancy camera",id:"-how-to-handle-a-fancy-camera",level:3},{value:"\ud83c\udf0e Setting up the world AND the camera",id:"-setting-up-the-world-and-the-camera",level:3},{value:"\ud83e\uddca Creating a cube",id:"-creating-a-cube",level:3},{value:"\ud83c\udf9f\ufe0f Using camera events",id:"\ufe0f-using-camera-events",level:3},{value:"\u23f1\ufe0f Measuring the performance (optional)",id:"\ufe0f-measuring-the-performance-optional",level:3},{value:"\ud83e\udde9 Building a camera UI",id:"-building-a-camera-ui",level:3},{value:"\ud83c\udf9b\ufe0f Navigation mode",id:"\ufe0f-navigation-mode",level:4},{value:"\ud83d\udcd0 Projections",id:"-projections",level:4},{value:"\u274c Toggling user input",id:"-toggling-user-input",level:4},{value:"\ud83d\udd0e Focusing objects",id:"-focusing-objects",level:4},{value:"\ud83c\udf89 Wrap up",id:"-wrap-up",level:3}],u={toc:m},d="wrapper";function h(e){var t=e.components,n=(0,r.Z)(e,i);return(0,a.kt)(d,(0,o.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("admonition",{title:"Source",type:"info"},(0,a.kt)("p",{parentName:"admonition"},"Copying and pasting? We've got you covered! You can find the full source code of this tutorial ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/ThatOpen/engine_components/blob/main/packages/core/src/core/OrthoPerspectiveCamera/example.ts"},"here"),".")),(0,a.kt)("h3",{id:"-how-to-handle-a-fancy-camera"},"\ud83d\udcf9 How to handle a fancy camera"),(0,a.kt)("hr",null),(0,a.kt)("p",null,"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. "),(0,a.kt)("admonition",{title:"Orthographic and Perspective cameras",type:"tip"},(0,a.kt)("p",{parentName:"admonition"},"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.")),(0,a.kt)("p",null,"In this tutorial, we will import:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"Three.js")," to get some 3D entities for our app."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"@thatopen/components")," to set up the barebone of our app."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"@thatopen/ui")," to add some simple and cool UI menus."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"Stats.js")," (optional) to measure the performance of our app.")),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-js"},'import Stats from "stats.js";\nimport * as THREE from "three";\nimport * as BUI from "@thatopen/ui";\nimport * as OBC from "@thatopen/components";\n')),(0,a.kt)("h3",{id:"-setting-up-the-world-and-the-camera"},"\ud83c\udf0e Setting up the world AND the camera"),(0,a.kt)("hr",null),(0,a.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. But there's one difference: we will use the OrthoPerspectiveCamera for initializing the world."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-js"},'const container = document.getElementById("container")!;\nlet components = new OBC.Components();\nlet worlds = components.get(OBC.Worlds);\n\nlet world = worlds.create<\n OBC.SimpleScene,\n OBC.OrthoPerspectiveCamera,\n OBC.SimpleRenderer\n>();\n\nworld.scene = new OBC.SimpleScene(components);\nworld.renderer = new OBC.SimpleRenderer(components, container);\nworld.camera = new OBC.OrthoPerspectiveCamera(components);\n\nworld.scene.setup();\n\nawait world.camera.controls.setLookAt(3, 3, 3, 0, 0, 0);\n\ncomponents.init();\n')),(0,a.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,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-js"},"world.scene.three.background = null;\n")),(0,a.kt)("p",null,"Easy, right? Believe it or not, this is all you need to use the OrthoPerspectiveCamera. Now, let's see it in action!"),(0,a.kt)("h3",{id:"-creating-a-cube"},"\ud83e\uddca Creating a cube"),(0,a.kt)("hr",null),(0,a.kt)("p",null,"We will start by creating a simple cube and a grid that will serve as a reference point for our camera."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-js"},'let cubeGeometry = new THREE.BoxGeometry();\nlet cubeMaterial = new THREE.MeshStandardMaterial({ color: "#6528D7" });\nlet cube = new THREE.Mesh(cubeGeometry, cubeMaterial);\ncube.position.set(0, 0.5, 0);\n\nworld.scene.three.add(cube);\nworld.meshes.add(cube);\n\nlet grids = components.get(OBC.Grids);\nlet grid = grids.create(world);\n')),(0,a.kt)("h3",{id:"\ufe0f-using-camera-events"},"\ud83c\udf9f\ufe0f Using camera events"),(0,a.kt)("hr",null),(0,a.kt)("p",null,"The OrthoPerspectiveCamera has a few events that you can use to manage the your scene. We will use the ",(0,a.kt)("inlineCode",{parentName:"p"},"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."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-js"},'world.camera.projection.onChanged.add(() => {\n const projection = world.camera.projection.current;\n grid.fade = projection === "Perspective";\n});\n')),(0,a.kt)("h3",{id:"\ufe0f-measuring-the-performance-optional"},"\u23f1\ufe0f Measuring the performance (optional)"),(0,a.kt)("hr",null),(0,a.kt)("p",null,"We'll use the ",(0,a.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,a.kt)("pre",null,(0,a.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,a.kt)("h3",{id:"-building-a-camera-ui"},"\ud83e\udde9 Building a camera UI"),(0,a.kt)("hr",null),(0,a.kt)("p",null,"Now we will use @thatopen/ui to create a simple UI for the OrthoPerspectiveCamera. It will have 4 elements: "),(0,a.kt)("h4",{id:"\ufe0f-navigation-mode"},"\ud83c\udf9b\ufe0f Navigation mode"),(0,a.kt)("p",null,"This will control the navigation mode of the OrthoPerspectiveCamera. It will have 3 options: "),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"Orbit"),": for 3D orbiting around the scene."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"FirstPerson"),": for navigating the scene in with the mouse wheel in first person."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"Plan"),": for navigating 2d plans (blocking the orbit).")),(0,a.kt)("h4",{id:"-projections"},"\ud83d\udcd0 Projections"),(0,a.kt)("p",null,"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!"),(0,a.kt)("h4",{id:"-toggling-user-input"},"\u274c Toggling user input"),(0,a.kt)("p",null,"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 ",(0,a.kt)("inlineCode",{parentName:"p"},"setUserInput")," method to toggle this."),(0,a.kt)("h4",{id:"-focusing-objects"},"\ud83d\udd0e Focusing objects"),(0,a.kt)("p",null,"The OrthoPerspectiveCamera has a ",(0,a.kt)("inlineCode",{parentName:"p"},"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."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-js"},'BUI.Manager.init();\n\nconst panel = BUI.Component.create(() => {\n return BUI.html`\n \n \n \n \n\n \n \n \n \n \n \n \n \n \n \n\n \n \n \n \n \n \n {\n const projection = world.camera.projection.current;\n grid.fade = projection === "Perspective";\n });\n }}"> \n \n\n \n \n `;\n});\n\ndocument.body.append(panel);\n')),(0,a.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,a.kt)("pre",null,(0,a.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,a.kt)("h3",{id:"-wrap-up"},"\ud83c\udf89 Wrap up"),(0,a.kt)("hr",null),(0,a.kt)("p",null,"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!"),(0,a.kt)("iframe",{src:"https://thatopen.github.io/engine_components/examples/OrthoPerspectiveCamera"}))}h.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/build/assets/js/035e0bbb.6649b65c.js b/build/assets/js/035e0bbb.6649b65c.js deleted file mode 100644 index ddfe2d908..000000000 --- a/build/assets/js/035e0bbb.6649b65c.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkdocs=self.webpackChunkdocs||[]).push([[5269],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,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 l=o.createContext({}),c=function(e){var t=o.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},p=function(e){var t=c(e.components);return o.createElement(l.Provider,{value:t},e.children)},m="mdxType",u={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},d=o.forwardRef((function(e,t){var n=e.components,a=e.mdxType,r=e.originalType,l=e.parentName,p=s(e,["components","mdxType","originalType","parentName"]),m=c(n),d=a,h=m["".concat(l,".").concat(d)]||m[d]||u[d]||r;return n?o.createElement(h,i(i({ref:t},p),{},{components:n})):o.createElement(h,i({ref:t},p))}));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]=d;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[m]="string"==typeof e?e:a,i[1]=s;for(var c=2;c{n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>l,default:()=>h,frontMatter:()=>s,metadata:()=>c,toc:()=>m});var o=n(7462),a=n(3366),r=(n(7294),n(3905)),i=["components"],s={},l=void 0,c={unversionedId:"Tutorials/Components/Core/OrthoPerspectiveCamera",id:"Tutorials/Components/Core/OrthoPerspectiveCamera",title:"OrthoPerspectiveCamera",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/OrthoPerspectiveCamera.mdx",sourceDirName:"Tutorials/Components/Core",slug:"/Tutorials/Components/Core/OrthoPerspectiveCamera",permalink:"/Tutorials/Components/Core/OrthoPerspectiveCamera",draft:!1,tags:[],version:"current",frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"MiniMap",permalink:"/Tutorials/Components/Core/MiniMap"},next:{title:"Raycasters",permalink:"/Tutorials/Components/Core/Raycasters"}},p={},m=[{value:"\ud83d\udcf9 How to handle a fancy camera",id:"-how-to-handle-a-fancy-camera",level:3},{value:"\ud83c\udf0e Setting up the world AND the camera",id:"-setting-up-the-world-and-the-camera",level:3},{value:"\ud83e\uddca Creating a cube",id:"-creating-a-cube",level:3},{value:"\ud83c\udf9f\ufe0f Using camera events",id:"\ufe0f-using-camera-events",level:3},{value:"\u23f1\ufe0f Measuring the performance (optional)",id:"\ufe0f-measuring-the-performance-optional",level:3},{value:"\ud83e\udde9 Building a camera UI",id:"-building-a-camera-ui",level:3},{value:"\ud83c\udf9b\ufe0f Navigation mode",id:"\ufe0f-navigation-mode",level:4},{value:"\ud83d\udcd0 Projections",id:"-projections",level:4},{value:"\u274c Toggling user input",id:"-toggling-user-input",level:4},{value:"\ud83d\udd0e Focusing objects",id:"-focusing-objects",level:4},{value:"\ud83c\udf89 Wrap up",id:"-wrap-up",level:3}],u={toc:m},d="wrapper";function h(e){var t=e.components,n=(0,a.Z)(e,i);return(0,r.kt)(d,(0,o.Z)({},u,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/core/OrthoPerspectiveCamera/example.ts"},"here"),".")),(0,r.kt)("h3",{id:"-how-to-handle-a-fancy-camera"},"\ud83d\udcf9 How to handle a fancy camera"),(0,r.kt)("hr",null),(0,r.kt)("p",null,"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. "),(0,r.kt)("admonition",{title:"Orthographic and Perspective cameras",type:"tip"},(0,r.kt)("p",{parentName:"admonition"},"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.")),(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"},"Three.js")," to get some 3D entities for our app."),(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"},"@thatopen/ui")," to add some simple and cool UI menus."),(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 THREE from "three";\nimport * as BUI from "@thatopen/ui";\nimport * as OBC from "@thatopen/components";\n')),(0,r.kt)("h3",{id:"-setting-up-the-world-and-the-camera"},"\ud83c\udf0e Setting up the world AND the camera"),(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. But there's one difference: we will use the OrthoPerspectiveCamera for initializing the world."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},'const container = document.getElementById("container")!;\nconst components = new OBC.Components();\nconst worlds = components.get(OBC.Worlds);\n\nconst world = worlds.create<\n OBC.SimpleScene,\n OBC.OrthoPerspectiveCamera,\n OBC.SimpleRenderer\n>();\n\nworld.scene = new OBC.SimpleScene(components);\nworld.renderer = new OBC.SimpleRenderer(components, container);\nworld.camera = new OBC.OrthoPerspectiveCamera(components);\n\nworld.scene.setup();\n\nawait world.camera.controls.setLookAt(3, 3, 3, 0, 0, 0);\n\ncomponents.init();\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)("p",null,"Easy, right? Believe it or not, this is all you need to use the OrthoPerspectiveCamera. Now, let's see it in action!"),(0,r.kt)("h3",{id:"-creating-a-cube"},"\ud83e\uddca Creating a cube"),(0,r.kt)("hr",null),(0,r.kt)("p",null,"We will start by creating a simple cube and a grid that will serve as a reference point for our camera."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},'const cubeGeometry = new THREE.BoxGeometry();\nconst cubeMaterial = new THREE.MeshStandardMaterial({ color: "#6528D7" });\nconst cube = new THREE.Mesh(cubeGeometry, cubeMaterial);\ncube.position.set(0, 0.5, 0);\n\nworld.scene.three.add(cube);\nworld.meshes.add(cube);\n\nconst grids = components.get(OBC.Grids);\nconst grid = grids.create(world);\n')),(0,r.kt)("h3",{id:"\ufe0f-using-camera-events"},"\ud83c\udf9f\ufe0f Using camera events"),(0,r.kt)("hr",null),(0,r.kt)("p",null,"The OrthoPerspectiveCamera has a few events that you can use to manage the your scene. We will use the ",(0,r.kt)("inlineCode",{parentName:"p"},"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."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},'world.camera.projection.onChanged.add(() => {\n const projection = world.camera.projection.current;\n grid.fade = projection === "Perspective";\n});\n')),(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:"-building-a-camera-ui"},"\ud83e\udde9 Building a camera UI"),(0,r.kt)("hr",null),(0,r.kt)("p",null,"Now we will use @thatopen/ui to create a simple UI for the OrthoPerspectiveCamera. It will have 4 elements: "),(0,r.kt)("h4",{id:"\ufe0f-navigation-mode"},"\ud83c\udf9b\ufe0f Navigation mode"),(0,r.kt)("p",null,"This will control the navigation mode of the OrthoPerspectiveCamera. It will have 3 options: "),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"Orbit"),": for 3D orbiting around the scene."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"FirstPerson"),": for navigating the scene in with the mouse wheel in first person."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"Plan"),": for navigating 2d plans (blocking the orbit).")),(0,r.kt)("h4",{id:"-projections"},"\ud83d\udcd0 Projections"),(0,r.kt)("p",null,"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!"),(0,r.kt)("h4",{id:"-toggling-user-input"},"\u274c Toggling user input"),(0,r.kt)("p",null,"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 ",(0,r.kt)("inlineCode",{parentName:"p"},"setUserInput")," method to toggle this."),(0,r.kt)("h4",{id:"-focusing-objects"},"\ud83d\udd0e Focusing objects"),(0,r.kt)("p",null,"The OrthoPerspectiveCamera has a ",(0,r.kt)("inlineCode",{parentName:"p"},"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."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},'BUI.Manager.init();\n\nconst panel = BUI.Component.create(() => {\n return BUI.html`\n \n \n \n \n\n \n \n \n \n \n \n \n \n \n \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! 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!"),(0,r.kt)("iframe",{src:"https://thatopen.github.io/engine_components/examples/OrthoPerspectiveCamera"}))}h.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/build/assets/js/0dcd76a4.8f965752.js b/build/assets/js/0dcd76a4.8f965752.js new file mode 100644 index 000000000..457d2ab42 --- /dev/null +++ b/build/assets/js/0dcd76a4.8f965752.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkdocs=self.webpackChunkdocs||[]).push([[7116],{3905:(e,t,n)=>{n.d(t,{Zo:()=>c,kt:()=>b});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},c=function(e){var t=p(e.components);return o.createElement(s.Provider,{value:t},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},m=o.forwardRef((function(e,t){var n=e.components,a=e.mdxType,r=e.originalType,s=e.parentName,c=l(e,["components","mdxType","originalType","parentName"]),u=p(n),m=a,b=u["".concat(s,".").concat(m)]||u[m]||d[m]||r;return n?o.createElement(b,i(i({ref:t},c),{},{components:n})):o.createElement(b,i({ref:t},c))}));function b(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var r=n.length,i=new Array(r);i[0]=m;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l[u]="string"==typeof e?e:a,i[1]=l;for(var p=2;p{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>s,default:()=>b,frontMatter:()=>l,metadata:()=>p,toc:()=>u});var o=n(7462),a=n(3366),r=(n(7294),n(3905)),i=["components"],l={},s=void 0,p={unversionedId:"Tutorials/UserInterface/OBC/EntityAttributes",id:"Tutorials/UserInterface/OBC/EntityAttributes",title:"EntityAttributes",description:"Copying and pasting? We've got you covered! You can find the full source code of this tutorial here.",source:"@site/docs/Tutorials/UserInterface/OBC/EntityAttributes.mdx",sourceDirName:"Tutorials/UserInterface/OBC",slug:"/Tutorials/UserInterface/OBC/EntityAttributes",permalink:"/Tutorials/UserInterface/OBC/EntityAttributes",draft:!1,tags:[],version:"current",frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"ElementProperties",permalink:"/Tutorials/UserInterface/OBC/ElementProperties"},next:{title:"ModelsList",permalink:"/Tutorials/UserInterface/OBC/ModelsList"}},c={},u=[{value:"Displaying data the advanced way \ud83d\udd25\ud83d\udd25",id:"displaying-data-the-advanced-way-",level:2},{value:"Loading a model and computing it's relations",id:"loading-a-model-and-computing-its-relations",level:3},{value:"Preconfiguring the table",id:"preconfiguring-the-table",level:3},{value:"Creating a panel to append the table",id:"creating-a-panel-to-append-the-table",level:3}],d={toc:u},m="wrapper";function b(e){var t=e.components,n=(0,a.Z)(e,i);return(0,r.kt)(m,(0,o.Z)({},d,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_ui-components/blob/main/packages/obc/src/components/tables/EntityAttributes/example.ts"},"here"),".")),(0,r.kt)("h2",{id:"displaying-data-the-advanced-way-"},"Displaying data the advanced way \ud83d\udd25\ud83d\udd25"),(0,r.kt)("hr",null),(0,r.kt)("p",null,"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!"),(0,r.kt)("h3",{id:"loading-a-model-and-computing-its-relations"},"Loading a model and computing it's relations"),(0,r.kt)("p",null,"First things first... let's load a model \ud83d\udc47"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},'import * as WEBIFC from "web-ifc";\nimport * as BUI from "@thatopen/ui";\nimport * as OBC from "@thatopen/components";\nimport * as OBF from "@thatopen/components-front";\nimport * as CUI from "../..";\n\nBUI.Manager.init();\n\nconst components = new OBC.Components();\n\nconst worlds = components.get(OBC.Worlds);\n\nconst world = worlds.create();\nconst sceneComponent = new OBC.SimpleScene(components);\nsceneComponent.setup();\nworld.scene = sceneComponent;\n\nconst viewport = document.createElement("bim-viewport");\nconst rendererComponent = new OBC.SimpleRenderer(components, viewport);\nworld.renderer = rendererComponent;\n\nconst cameraComponent = new OBC.SimpleCamera(components);\nworld.camera = cameraComponent;\ncameraComponent.controls.setLookAt(10, 5.5, 5, -4, -1, -6.5);\n\nviewport.addEventListener("resize", () => {\n rendererComponent.resize();\n cameraComponent.updateAspect();\n});\n\ncomponents.init();\n\nconst grids = components.get(OBC.Grids);\ngrids.create(world);\n')),(0,r.kt)("admonition",{type:"tip"},(0,r.kt)("p",{parentName:"admonition"},"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?")),(0,r.kt)("p",null,"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 ",(0,r.kt)("a",{parentName:"p",href:"/Tutorials/Components/Core/IfcRelationsIndexer"},"IfcRelationsIndexer")," component from ",(0,r.kt)("inlineCode",{parentName:"p"},"@thatopen/components")," to speed up the process."),(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(\n "https://thatopen.github.io/engine_ui-components/resources/small.ifc",\n);\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:"preconfiguring-the-table"},"Preconfiguring the table"),(0,r.kt)("p",null,"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:"),(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,"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 ",(0,r.kt)("inlineCode",{parentName:"p"},"html")," template result."),(0,r.kt)("admonition",{type:"tip"},(0,r.kt)("p",{parentName:"admonition"},"If you want to learn more about the ",(0,r.kt)("inlineCode",{parentName:"p"},"html")," template tag and how to use it, just take a look at the tutorial on how to make a custom component.")),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},'const baseStyle: Record = {\n padding: "0.25rem",\n borderRadius: "0.25rem",\n};\n')),(0,r.kt)("p",null,"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 \ud83d\ude42"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},'const tableDefinition: BUI.TableDataTransform = {\n Entity: (entity) => {\n let style = {};\n if (entity === OBC.IfcCategoryMap[WEBIFC.IFCPROPERTYSET]) {\n style = {\n ...baseStyle,\n backgroundColor: "purple",\n color: "white",\n };\n }\n if (String(entity).includes("IFCWALL")) {\n style = {\n ...baseStyle,\n backgroundColor: "green",\n color: "white",\n };\n }\n return BUI.html`${entity}`;\n },\n PredefinedType: (type) => {\n const colors = ["#1c8d83", "#3c1c8d", "#386c19", "#837c24"];\n const randomIndex = Math.floor(Math.random() * colors.length);\n const backgroundColor = colors[randomIndex];\n const style = { ...baseStyle, backgroundColor, color: "white" };\n return BUI.html`${type}`;\n },\n NominalValue: (value) => {\n let style = {};\n if (typeof value === "boolean" && value === false) {\n style = { ...baseStyle, backgroundColor: "#b13535", color: "white" };\n }\n if (typeof value === "boolean" && value === true) {\n style = { ...baseStyle, backgroundColor: "#18882c", color: "white" };\n }\n return BUI.html`${value}`;\n },\n};\n')),(0,r.kt)("p",null,"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 ",(0,r.kt)("a",{parentName:"p",href:"/Tutorials/Components/Front/Highlighter"},"Highlighter"),":"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},'const [attributesTable, updateAttributesTable] = CUI.tables.entityAttributes({\n components,\n fragmentIdMap: {},\n tableDefinition,\n attributesToInclude: () => {\n const attributes: any[] = [\n "Name",\n "ContainedInStructure",\n "HasProperties",\n "HasPropertySets",\n (name: string) => name.includes("Value"),\n (name: string) => name.startsWith("Material"),\n (name: string) => name.startsWith("Relating"),\n (name: string) => {\n const ignore = ["IsGroupedBy", "IsDecomposedBy"];\n return name.startsWith("Is") && !ignore.includes(name);\n },\n ];\n return attributes;\n },\n});\n\nattributesTable.expanded = true;\nattributesTable.indentationInText = true;\nattributesTable.preserveStructureOnFilter = true;\n')),(0,r.kt)("h3",{id:"creating-a-panel-to-append-the-table"},"Creating a panel to append the table"),(0,r.kt)("p",null,"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 \ud83d\ude09"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},"const highlighter = components.get(OBF.Highlighter);\nhighlighter.setup({ world });\n\nhighlighter.events.select.onHighlight.add((fragmentIdMap) => {\n updateAttributesTable({ fragmentIdMap });\n});\n\nhighlighter.events.select.onClear.add(() =>\n updateAttributesTable({ fragmentIdMap: {} }),\n);\n")),(0,r.kt)("p",null,"Finally, let's create a BIM Grid element and provide both the panel and the viewport to display everything."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},'const entityAttributesPanel = BUI.Component.create(() => {\n const onSearchInput = (e: Event) => {\n const input = e.target as BUI.TextInput;\n attributesTable.queryString = input.value;\n };\n\n const onPreserveStructureChange = (e: Event) => {\n const checkbox = e.target as BUI.Checkbox;\n attributesTable.preserveStructureOnFilter = checkbox.checked;\n };\n\n const onExportJSON = () => {\n attributesTable.downloadData("entities-attributes");\n };\n\n const onCopyTSV = async () => {\n await navigator.clipboard.writeText(attributesTable.tsv);\n alert(\n "Table data copied as TSV in clipboard! Try to paste it in a spreadsheet app.",\n );\n };\n\n const onAttributesChange = (e: Event) => {\n const dropdown = e.target as BUI.Dropdown;\n updateAttributesTable({\n attributesToInclude: () => {\n const attributes: any[] = [\n ...dropdown.value,\n (name: string) => name.includes("Value"),\n (name: string) => name.startsWith("Material"),\n (name: string) => name.startsWith("Relating"),\n (name: string) => {\n const ignore = ["IsGroupedBy", "IsDecomposedBy"];\n return name.startsWith("Is") && !ignore.includes(name);\n },\n ];\n return attributes;\n },\n });\n };\n\n return BUI.html`\n \n \n
\n
\n \n \n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n ${attributesTable}\n
\n
\n `;\n});\n')),(0,r.kt)("p",null,"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! \ud83d\udcaa"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},'const app = document.createElement("bim-grid");\napp.layouts = {\n main: {\n template: `\n "viewport" 1fr\n "entityAttributesPanel" 1fr\n `,\n elements: { entityAttributesPanel, viewport },\n },\n};\n\napp.layout = "main";\ndocument.body.append(app);\n')),(0,r.kt)("iframe",{src:"https://thatopen.github.io/engine_ui-components/examples/EntityAttributes"}))}b.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/build/assets/js/0dcd76a4.beefad49.js b/build/assets/js/0dcd76a4.beefad49.js deleted file mode 100644 index a5051ff62..000000000 --- a/build/assets/js/0dcd76a4.beefad49.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkdocs=self.webpackChunkdocs||[]).push([[7116],{3905:(e,t,n)=>{n.d(t,{Zo:()=>c,kt:()=>b});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},c=function(e){var t=p(e.components);return o.createElement(s.Provider,{value:t},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},m=o.forwardRef((function(e,t){var n=e.components,a=e.mdxType,r=e.originalType,s=e.parentName,c=l(e,["components","mdxType","originalType","parentName"]),u=p(n),m=a,b=u["".concat(s,".").concat(m)]||u[m]||d[m]||r;return n?o.createElement(b,i(i({ref:t},c),{},{components:n})):o.createElement(b,i({ref:t},c))}));function b(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var r=n.length,i=new Array(r);i[0]=m;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l[u]="string"==typeof e?e:a,i[1]=l;for(var p=2;p{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>s,default:()=>b,frontMatter:()=>l,metadata:()=>p,toc:()=>u});var o=n(7462),a=n(3366),r=(n(7294),n(3905)),i=["components"],l={},s=void 0,p={unversionedId:"Tutorials/UserInterface/OBC/EntityAttributes",id:"Tutorials/UserInterface/OBC/EntityAttributes",title:"EntityAttributes",description:"Copying and pasting? We've got you covered! You can find the full source code of this tutorial here.",source:"@site/docs/Tutorials/UserInterface/OBC/EntityAttributes.mdx",sourceDirName:"Tutorials/UserInterface/OBC",slug:"/Tutorials/UserInterface/OBC/EntityAttributes",permalink:"/Tutorials/UserInterface/OBC/EntityAttributes",draft:!1,tags:[],version:"current",frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"ElementProperties",permalink:"/Tutorials/UserInterface/OBC/ElementProperties"},next:{title:"ModelsList",permalink:"/Tutorials/UserInterface/OBC/ModelsList"}},c={},u=[{value:"Displaying data the advanced way \ud83d\udd25\ud83d\udd25",id:"displaying-data-the-advanced-way-",level:2},{value:"Loading a model and computing it's relations",id:"loading-a-model-and-computing-its-relations",level:3},{value:"Preconfiguring the table",id:"preconfiguring-the-table",level:3},{value:"Creating a panel to append the table",id:"creating-a-panel-to-append-the-table",level:3}],d={toc:u},m="wrapper";function b(e){var t=e.components,n=(0,a.Z)(e,i);return(0,r.kt)(m,(0,o.Z)({},d,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_ui-components/blob/main/packages/obc/src/components/tables/EntityAttributes/example.ts"},"here"),".")),(0,r.kt)("h2",{id:"displaying-data-the-advanced-way-"},"Displaying data the advanced way \ud83d\udd25\ud83d\udd25"),(0,r.kt)("hr",null),(0,r.kt)("p",null,"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!"),(0,r.kt)("h3",{id:"loading-a-model-and-computing-its-relations"},"Loading a model and computing it's relations"),(0,r.kt)("p",null,"First things first... let's load a model \ud83d\udc47"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},'import * as WEBIFC from "web-ifc";\nimport * as BUI from "@thatopen/ui";\nimport * as OBC from "@thatopen/components";\nimport * as OBF from "@thatopen/components-front";\nimport * as CUI from "../..";\n\nBUI.Manager.init();\n\nconst components = new OBC.Components();\n\nconst worlds = components.get(OBC.Worlds);\n\nconst world = worlds.create();\nconst sceneComponent = new OBC.SimpleScene(components);\nsceneComponent.setup();\nworld.scene = sceneComponent;\n\nconst viewport = document.createElement("bim-viewport");\nconst rendererComponent = new OBC.SimpleRenderer(components, viewport);\nworld.renderer = rendererComponent;\n\nconst cameraComponent = new OBC.SimpleCamera(components);\nworld.camera = cameraComponent;\ncameraComponent.controls.setLookAt(10, 5.5, 5, -4, -1, -6.5);\n\nviewport.addEventListener("resize", () => {\n rendererComponent.resize();\n cameraComponent.updateAspect();\n});\n\ncomponents.init();\n\nconst grids = components.get(OBC.Grids);\ngrids.create(world);\n')),(0,r.kt)("admonition",{type:"tip"},(0,r.kt)("p",{parentName:"admonition"},"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?")),(0,r.kt)("p",null,"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 ",(0,r.kt)("a",{parentName:"p",href:"/Tutorials/Components/Core/IfcRelationsIndexer"},"IfcRelationsIndexer")," component from ",(0,r.kt)("inlineCode",{parentName:"p"},"@thatopen/components")," to speed up the process."),(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("/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:"preconfiguring-the-table"},"Preconfiguring the table"),(0,r.kt)("p",null,"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:"),(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,"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 ",(0,r.kt)("inlineCode",{parentName:"p"},"html")," template result."),(0,r.kt)("admonition",{type:"tip"},(0,r.kt)("p",{parentName:"admonition"},"If you want to learn more about the ",(0,r.kt)("inlineCode",{parentName:"p"},"html")," template tag and how to use it, just take a look at the tutorial on how to make a custom component.")),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},'const baseStyle: Record = {\n padding: "0.25rem",\n borderRadius: "0.25rem",\n};\n')),(0,r.kt)("p",null,"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 \ud83d\ude42"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},'const tableDefinition: BUI.TableDefinition = {\n Entity: (entity) => {\n let style = {};\n if (entity === OBC.IfcCategoryMap[WEBIFC.IFCPROPERTYSET]) {\n style = {\n ...baseStyle,\n backgroundColor: "purple",\n color: "white",\n };\n }\n if (String(entity).includes("IFCWALL")) {\n style = {\n ...baseStyle,\n backgroundColor: "green",\n color: "white",\n };\n }\n return BUI.html``;\n },\n PredefinedType: (type) => {\n const colors = ["#1c8d83", "#3c1c8d", "#386c19", "#837c24"];\n const randomIndex = Math.floor(Math.random() * colors.length);\n const backgroundColor = colors[randomIndex];\n const style = { ...baseStyle, backgroundColor, color: "white" };\n return BUI.html``;\n },\n NominalValue: (value) => {\n let style = {};\n if (typeof value === "boolean" && value === false) {\n style = { ...baseStyle, backgroundColor: "#b13535", color: "white" };\n }\n if (typeof value === "boolean" && value === true) {\n style = { ...baseStyle, backgroundColor: "#18882c", color: "white" };\n }\n return BUI.html``;\n },\n};\n')),(0,r.kt)("p",null,"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 ",(0,r.kt)("a",{parentName:"p",href:"/Tutorials/Components/Front/Highlighter"},"Highlighter"),":"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},'const [attributesTable, updateAttributesTable] = CUI.tables.entityAttributes({\n components,\n fragmentIdMap: {},\n tableDefinition,\n attributesToInclude: () => {\n const attributes: any[] = [\n "Name",\n "ContainedInStructure",\n "HasProperties",\n "HasPropertySets",\n (name: string) => name.includes("Value"),\n (name: string) => name.startsWith("Material"),\n (name: string) => name.startsWith("Relating"),\n (name: string) => {\n const ignore = ["IsGroupedBy", "IsDecomposedBy"];\n return name.startsWith("Is") && !ignore.includes(name);\n },\n ];\n return attributes;\n },\n});\n\nattributesTable.expanded = true;\nattributesTable.indentationInText = true;\nattributesTable.preserveStructureOnFilter = true;\n')),(0,r.kt)("h3",{id:"creating-a-panel-to-append-the-table"},"Creating a panel to append the table"),(0,r.kt)("p",null,"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 \ud83d\ude09"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},"const highlighter = components.get(OBF.Highlighter);\nhighlighter.setup({ world });\n\nhighlighter.events.select.onHighlight.add((fragmentIdMap) => {\n updateAttributesTable({ fragmentIdMap });\n});\n\nhighlighter.events.select.onClear.add(() =>\n updateAttributesTable({ fragmentIdMap: {} }),\n);\n")),(0,r.kt)("p",null,"Finally, let's create a BIM Grid element and provide both the panel and the viewport to display everything."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},'const entityAttributesPanel = BUI.Component.create(() => {\n const onSearchInput = (e: Event) => {\n const input = e.target as BUI.TextInput;\n attributesTable.queryString = input.value;\n };\n\n const onPreserveStructureChange = (e: Event) => {\n const checkbox = e.target as BUI.Checkbox;\n attributesTable.preserveStructureOnFilter = checkbox.checked;\n };\n\n const onExportJSON = () => {\n attributesTable.downloadData("entities-attributes");\n };\n\n const onCopyTSV = async () => {\n await navigator.clipboard.writeText(attributesTable.tsv);\n alert(\n "Table data copied as TSV in clipboard! Try to paste it in a spreadsheet app.",\n );\n };\n\n const onAttributesChange = (e: Event) => {\n const dropdown = e.target as BUI.Dropdown;\n updateAttributesTable({\n attributesToInclude: () => {\n const attributes: any[] = [\n ...dropdown.value,\n (name: string) => name.includes("Value"),\n (name: string) => name.startsWith("Material"),\n (name: string) => name.startsWith("Relating"),\n (name: string) => {\n const ignore = ["IsGroupedBy", "IsDecomposedBy"];\n return name.startsWith("Is") && !ignore.includes(name);\n },\n ];\n return attributes;\n },\n });\n };\n\n return BUI.html`\n \n \n
\n
\n \n \n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n ${attributesTable}\n
\n
\n `;\n});\n')),(0,r.kt)("p",null,"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! \ud83d\udcaa"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},'const app = document.createElement("bim-grid");\napp.layouts = {\n main: {\n template: `\n "viewport" 1fr\n "entityAttributesPanel" 1fr\n `,\n elements: { entityAttributesPanel, viewport },\n },\n};\n\napp.layout = "main";\ndocument.body.append(app);\n')),(0,r.kt)("iframe",{src:"https://thatopen.github.io/engine_ui-components/examples/EntityAttributes"}))}b.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/build/assets/js/2a9a2072.252e9c72.js b/build/assets/js/2a9a2072.252e9c72.js new file mode 100644 index 000000000..40961fa36 --- /dev/null +++ b/build/assets/js/2a9a2072.252e9c72.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkdocs=self.webpackChunkdocs||[]).push([[1860],{3905:(e,n,t)=>{t.d(n,{Zo:()=>d,kt:()=>h});var o=t(7294);function a(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function r(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);n&&(o=o.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,o)}return t}function i(e){for(var n=1;n=0||(a[t]=e[t]);return a}(e,n);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(o=0;o=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(a[t]=e[t])}return a}var s=o.createContext({}),p=function(e){var n=o.useContext(s),t=n;return e&&(t="function"==typeof e?e(n):i(i({},n),e)),t},d=function(e){var n=p(e.components);return o.createElement(s.Provider,{value:n},e.children)},u="mdxType",m={inlineCode:"code",wrapper:function(e){var n=e.children;return o.createElement(o.Fragment,{},n)}},c=o.forwardRef((function(e,n){var t=e.components,a=e.mdxType,r=e.originalType,s=e.parentName,d=l(e,["components","mdxType","originalType","parentName"]),u=p(t),c=a,h=u["".concat(s,".").concat(c)]||u[c]||m[c]||r;return t?o.createElement(h,i(i({ref:n},d),{},{components:t})):o.createElement(h,i({ref:n},d))}));function h(e,n){var t=arguments,a=n&&n.mdxType;if("string"==typeof e||a){var r=t.length,i=new Array(r);i[0]=c;var l={};for(var s in n)hasOwnProperty.call(n,s)&&(l[s]=n[s]);l.originalType=e,l[u]="string"==typeof e?e:a,i[1]=l;for(var p=2;p{t.r(n),t.d(n,{assets:()=>d,contentTitle:()=>s,default:()=>h,frontMatter:()=>l,metadata:()=>p,toc:()=>u});var o=t(7462),a=t(3366),r=(t(7294),t(3905)),i=["components"],l={},s=void 0,p={unversionedId:"Tutorials/Components/Core/BoundingBoxer",id:"Tutorials/Components/Core/BoundingBoxer",title:"BoundingBoxer",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/BoundingBoxer.mdx",sourceDirName:"Tutorials/Components/Core",slug:"/Tutorials/Components/Core/BoundingBoxer",permalink:"/Tutorials/Components/Core/BoundingBoxer",draft:!1,tags:[],version:"current",frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"Components",permalink:"/Tutorials/Components/"},next:{title:"Classifier",permalink:"/Tutorials/Components/Core/Classifier"}},d={},u=[{value:"\ud83e\uddca Playing with boxes",id:"-playing-with-boxes",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:"\ud83c\udfb2 Creation of Bounding Boxes",id:"-creation-of-bounding-boxes",level:3},{value:"\ud83d\udc53 Reading the Bounding Box data",id:"-reading-the-bounding-box-data",level:4},{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:u},c="wrapper";function h(e){var n=e.components,t=(0,a.Z)(e,i);return(0,r.kt)(c,(0,o.Z)({},m,t,{components:n,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/fragments/BoundingBoxer/example.ts"},"here"),".")),(0,r.kt)("h3",{id:"-playing-with-boxes"},"\ud83e\uddca Playing with boxes"),(0,r.kt)("hr",null),(0,r.kt)("p",null,"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."),(0,r.kt)("admonition",{title:"Bounding boxes?",type:"tip"},(0,r.kt)("p",{parentName:"admonition"},"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.")),(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/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)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"@thatopen/ui")," to add some simple and cool UI menus.")),(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 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."),(0,r.kt)("admonition",{title:"Fragments?",type:"tip"},(0,r.kt)("p",{parentName:"admonition"},"If you are not familiar with fragments, check out the IfcLoader tutorial!")),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},'const fragments = components.get(OBC.FragmentsManager);\nconst file = await fetch(\n "https://thatopen.github.io/engine_components/resources/small.frag",\n);\nconst data = await file.arrayBuffer();\nconst buffer = new Uint8Array(data);\nconst model = fragments.load(buffer);\nworld.scene.three.add(model);\n')),(0,r.kt)("h3",{id:"-creation-of-bounding-boxes"},"\ud83c\udfb2 Creation of Bounding Boxes"),(0,r.kt)("hr",null),(0,r.kt)("p",null,"Now that our setup is done, lets see how you can create the bounding boxes of the model.\nBIM models are complex, but don't worry: creating the ",(0,r.kt)("a",{parentName:"p",href:"https://threejs.org/docs/?q=bound#api/en/math/Box3"},"bounding boxes")," is a piece of cake thanks to the ",(0,r.kt)("inlineCode",{parentName:"p"},"BoundingBoxer"),".\ud83d\udcaa\nWe can add models to the computation of the bounding box simply by using the ",(0,r.kt)("inlineCode",{parentName:"p"},"add()")," method."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},"const fragmentBbox = components.get(OBC.BoundingBoxer);\nfragmentBbox.add(model);\n")),(0,r.kt)("h4",{id:"-reading-the-bounding-box-data"},"\ud83d\udc53 Reading the Bounding Box data"),(0,r.kt)("p",null,"After adding the model, we can now read the mesh from bounding box using ",(0,r.kt)("inlineCode",{parentName:"p"},"getMesh()")," method. "),(0,r.kt)("admonition",{title:"Don't forget to clean up after using it! \ud83e\uddf9",type:"tip"},(0,r.kt)("p",{parentName:"admonition"},"It's a good practice to reset the bounding box after using it with the ",(0,r.kt)("inlineCode",{parentName:"p"},"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).")),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},"const bbox = fragmentBbox.getMesh();\nfragmentBbox.reset();\n")),(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 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!"),(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\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! 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!"),(0,r.kt)("iframe",{src:"https://thatopen.github.io/engine_components/examples/BoundingBoxer"}))}h.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/build/assets/js/2a9a2072.2cb88fcf.js b/build/assets/js/2a9a2072.2cb88fcf.js deleted file mode 100644 index 89c4056b0..000000000 --- a/build/assets/js/2a9a2072.2cb88fcf.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkdocs=self.webpackChunkdocs||[]).push([[1860],{3905:(e,n,t)=>{t.d(n,{Zo:()=>d,kt:()=>h});var o=t(7294);function a(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function r(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);n&&(o=o.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,o)}return t}function i(e){for(var n=1;n=0||(a[t]=e[t]);return a}(e,n);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(o=0;o=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(a[t]=e[t])}return a}var s=o.createContext({}),p=function(e){var n=o.useContext(s),t=n;return e&&(t="function"==typeof e?e(n):i(i({},n),e)),t},d=function(e){var n=p(e.components);return o.createElement(s.Provider,{value:n},e.children)},u="mdxType",m={inlineCode:"code",wrapper:function(e){var n=e.children;return o.createElement(o.Fragment,{},n)}},c=o.forwardRef((function(e,n){var t=e.components,a=e.mdxType,r=e.originalType,s=e.parentName,d=l(e,["components","mdxType","originalType","parentName"]),u=p(t),c=a,h=u["".concat(s,".").concat(c)]||u[c]||m[c]||r;return t?o.createElement(h,i(i({ref:n},d),{},{components:t})):o.createElement(h,i({ref:n},d))}));function h(e,n){var t=arguments,a=n&&n.mdxType;if("string"==typeof e||a){var r=t.length,i=new Array(r);i[0]=c;var l={};for(var s in n)hasOwnProperty.call(n,s)&&(l[s]=n[s]);l.originalType=e,l[u]="string"==typeof e?e:a,i[1]=l;for(var p=2;p{t.r(n),t.d(n,{assets:()=>d,contentTitle:()=>s,default:()=>h,frontMatter:()=>l,metadata:()=>p,toc:()=>u});var o=t(7462),a=t(3366),r=(t(7294),t(3905)),i=["components"],l={},s=void 0,p={unversionedId:"Tutorials/Components/Core/BoundingBoxer",id:"Tutorials/Components/Core/BoundingBoxer",title:"BoundingBoxer",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/BoundingBoxer.mdx",sourceDirName:"Tutorials/Components/Core",slug:"/Tutorials/Components/Core/BoundingBoxer",permalink:"/Tutorials/Components/Core/BoundingBoxer",draft:!1,tags:[],version:"current",frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"Components",permalink:"/Tutorials/Components/"},next:{title:"Clipper",permalink:"/Tutorials/Components/Core/Clipper"}},d={},u=[{value:"\ud83e\uddca Playing with boxes",id:"-playing-with-boxes",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:"\ud83c\udfb2 Creation of Bounding Boxes",id:"-creation-of-bounding-boxes",level:3},{value:"\ud83d\udc53 Reading the Bounding Box data",id:"-reading-the-bounding-box-data",level:4},{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:u},c="wrapper";function h(e){var n=e.components,t=(0,a.Z)(e,i);return(0,r.kt)(c,(0,o.Z)({},m,t,{components:n,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/fragments/BoundingBoxer/example.ts"},"here"),".")),(0,r.kt)("h3",{id:"-playing-with-boxes"},"\ud83e\uddca Playing with boxes"),(0,r.kt)("hr",null),(0,r.kt)("p",null,"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."),(0,r.kt)("admonition",{title:"Bounding boxes?",type:"tip"},(0,r.kt)("p",{parentName:"admonition"},"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.")),(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/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)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"@thatopen/ui")," to add some simple and cool UI menus.")),(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 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."),(0,r.kt)("admonition",{title:"Fragments?",type:"tip"},(0,r.kt)("p",{parentName:"admonition"},"If you are not familiar with fragments, check out the IfcLoader tutorial!")),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},'const fragments = components.get(OBC.FragmentsManager);\nconst file = await fetch(\n "https://thatopen.github.io/engine_components/resources/small.frag",\n);\nconst data = await file.arrayBuffer();\nconst buffer = new Uint8Array(data);\nconst model = fragments.load(buffer);\nworld.scene.three.add(model);\n')),(0,r.kt)("h3",{id:"-creation-of-bounding-boxes"},"\ud83c\udfb2 Creation of Bounding Boxes"),(0,r.kt)("hr",null),(0,r.kt)("p",null,"Now that our setup is done, lets see how you can create the bounding boxes of the model.\nBIM models are complex, but don't worry: creating the ",(0,r.kt)("a",{parentName:"p",href:"https://threejs.org/docs/?q=bound#api/en/math/Box3"},"bounding boxes")," is a piece of cake thanks to the ",(0,r.kt)("inlineCode",{parentName:"p"},"BoundingBoxer"),".\ud83d\udcaa\nWe can add models to the computation of the bounding box simply by using the ",(0,r.kt)("inlineCode",{parentName:"p"},"add()")," method."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},"const fragmentBbox = components.get(OBC.BoundingBoxer);\nfragmentBbox.add(model);\n")),(0,r.kt)("h4",{id:"-reading-the-bounding-box-data"},"\ud83d\udc53 Reading the Bounding Box data"),(0,r.kt)("p",null,"After adding the model, we can now read the mesh from bounding box using ",(0,r.kt)("inlineCode",{parentName:"p"},"getMesh()")," method. "),(0,r.kt)("admonition",{title:"Don't forget to clean up after using it! \ud83e\uddf9",type:"tip"},(0,r.kt)("p",{parentName:"admonition"},"It's a good practice to reset the bounding box after using it with the ",(0,r.kt)("inlineCode",{parentName:"p"},"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).")),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},"const bbox = fragmentBbox.getMesh();\nfragmentBbox.reset();\n")),(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 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!"),(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\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! 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!"),(0,r.kt)("iframe",{src:"https://thatopen.github.io/engine_components/examples/BoundingBoxer"}))}h.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/build/assets/js/2aba4d5d.0179a92d.js b/build/assets/js/2aba4d5d.0179a92d.js new file mode 100644 index 000000000..624aadc95 --- /dev/null +++ b/build/assets/js/2aba4d5d.0179a92d.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkdocs=self.webpackChunkdocs||[]).push([[282],{3905:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>h});var r=n(7294);function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function l(e){for(var t=1;t=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var i=r.createContext({}),p=function(e){var t=r.useContext(i),n=t;return e&&(n="function"==typeof e?e(t):l(l({},t),e)),n},u=function(e){var t=p(e.components);return r.createElement(i.Provider,{value:t},e.children)},c="mdxType",m={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},d=r.forwardRef((function(e,t){var n=e.components,o=e.mdxType,a=e.originalType,i=e.parentName,u=s(e,["components","mdxType","originalType","parentName"]),c=p(n),d=o,h=c["".concat(i,".").concat(d)]||c[d]||m[d]||a;return n?r.createElement(h,l(l({ref:t},u),{},{components:n})):r.createElement(h,l({ref:t},u))}));function h(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var a=n.length,l=new Array(a);l[0]=d;var s={};for(var i in t)hasOwnProperty.call(t,i)&&(s[i]=t[i]);s.originalType=e,s[c]="string"==typeof e?e:o,l[1]=s;for(var p=2;p{n.r(t),n.d(t,{assets:()=>u,contentTitle:()=>i,default:()=>h,frontMatter:()=>s,metadata:()=>p,toc:()=>c});var r=n(7462),o=n(3366),a=(n(7294),n(3905)),l=["components"],s={},i=void 0,p={unversionedId:"Tutorials/Components/Core/Cullers",id:"Tutorials/Components/Core/Cullers",title:"Cullers",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/Cullers.mdx",sourceDirName:"Tutorials/Components/Core",slug:"/Tutorials/Components/Core/Cullers",permalink:"/Tutorials/Components/Core/Cullers",draft:!1,tags:[],version:"current",frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"Clipper",permalink:"/Tutorials/Components/Core/Clipper"},next:{title:"Exploder",permalink:"/Tutorials/Components/Core/Exploder"}},u={},c=[{value:"\ud83d\ude85 Managing Performance",id:"-managing-performance",level:3},{value:"\ud83c\udf0e Setting up a simple scene",id:"-setting-up-a-simple-scene",level:3},{value:"\ud83e\uddf0 Creating Screen Culler",id:"-creating-screen-culler",level:3},{value:"\ud83e\uddf1 Adding a ton of cubes",id:"-adding-a-ton-of-cubes",level:3},{value:"\ud83d\udd04\ufe0f Updating the Culler",id:"\ufe0f-updating-the-culler",level:3},{value:"\u23f1\ufe0f Measuring the performance (optional)",id:"\ufe0f-measuring-the-performance-optional",level:3}],m={toc:c},d="wrapper";function h(e){var t=e.components,n=(0,o.Z)(e,l);return(0,a.kt)(d,(0,r.Z)({},m,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("admonition",{title:"Source",type:"info"},(0,a.kt)("p",{parentName:"admonition"},"Copying and pasting? We've got you covered! You can find the full source code of this tutorial ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/ThatOpen/engine_components/blob/main/packages/core/src/core/Cullers/example.ts"},"here"),".")),(0,a.kt)("h3",{id:"-managing-performance"},"\ud83d\ude85 Managing Performance"),(0,a.kt)("hr",null),(0,a.kt)("p",null,"There are occasions when your scene has too many objects. Multiple objects being rendered simultaneously ",(0,a.kt)("strong",{parentName:"p"},"lengthens computation time"),"\u231b\ufe0f and ",(0,a.kt)("strong",{parentName:"p"},"degrades performance"),".\ud83c\udf21\ufe0f In this tutorial, we will use ",(0,a.kt)("strong",{parentName:"p"},"ScreenCuller")," to improve performance by reducing unnecessary computations.\ud83d\ude80"),(0,a.kt)("admonition",{title:'What\'s "culling"?',type:"tip"},(0,a.kt)("p",{parentName:"admonition"},"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.")),(0,a.kt)("p",null,"In this tutorial, we will import:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"Three.js")," to get some 3D entities for our app."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"@thatopen/components")," to set up the barebone of our app."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"Stats.js")," (optional) to measure the performance of our app.")),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-js"},'import * as THREE from "three";\nimport Stats from "stats.js";\nimport * as OBC from "../..";\n')),(0,a.kt)("h3",{id:"-setting-up-a-simple-scene"},"\ud83c\udf0e Setting up a simple scene"),(0,a.kt)("hr",null),(0,a.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,a.kt)("pre",null,(0,a.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);\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(13, 13, 13, 0, 0, 0);\n\nworld.scene.setup();\n\nconst grids = components.get(OBC.Grids);\ngrids.create(world);\n')),(0,a.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,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-js"},"world.scene.three.background = null;\n")),(0,a.kt)("h3",{id:"-creating-screen-culler"},"\ud83e\uddf0 Creating Screen Culler"),(0,a.kt)("hr",null),(0,a.kt)("p",null,"Although adding Screen Culler to your project can appear difficult, it is actually very easy. We just need to get the ",(0,a.kt)("inlineCode",{parentName:"p"},"Cullers")," component and create a new instance of ",(0,a.kt)("inlineCode",{parentName:"p"},"ScreenCuller"),". Remember that although you can instance the Cullers component, it's better to get it from the ",(0,a.kt)("inlineCode",{parentName:"p"},"components")," object, as all the components are meant to be singletons within a Component instance, and this ensures that."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-js"},"const cullers = components.get(OBC.Cullers);\nconst culler = cullers.create(world);\n")),(0,a.kt)("p",null,"You can use the ",(0,a.kt)("inlineCode",{parentName:"p"},"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:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-js"},"culler.threshold = 200;\n")),(0,a.kt)("p",null,"Additionally, we will activate the ",(0,a.kt)("inlineCode",{parentName:"p"},"culler.renderDebugFrame")," so that we can see the 2D screen of the elements that are not occluded. We will get the ",(0,a.kt)("strong",{parentName:"p"},"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 ",(0,a.kt)("inlineCode",{parentName:"p"},'debugFrame.style.visibility = "collapse";')," line."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-js"},'culler.renderDebugFrame = true;\nconst debugFrame = culler.renderer.domElement;\ndocument.body.appendChild(debugFrame);\ndebugFrame.style.position = "fixed";\ndebugFrame.style.left = "0";\ndebugFrame.style.bottom = "0";\ndebugFrame.style.visibility = "collapse";\n')),(0,a.kt)("h3",{id:"-adding-a-ton-of-cubes"},"\ud83e\uddf1 Adding a ton of cubes"),(0,a.kt)("p",null,"We'll add the Simple 3D Cube and do it ",(0,a.kt)("strong",{parentName:"p"},"300 times"),"!\ud83e\udd2f We'll generate box geometry and use Lambert material."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-js"},'const cubes: THREE.Mesh[] = [];\nconst geometry = new THREE.BoxGeometry(2, 2, 2);\nconst material = new THREE.MeshLambertMaterial({ color: "#6528D7" });\n')),(0,a.kt)("admonition",{title:"Randomising the Cubes Placement",type:"info"},(0,a.kt)("p",{parentName:"admonition"},"We'll write a quick ",(0,a.kt)("strong",{parentName:"p"},"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.\ud83d\udccc")),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-js"},"function getRandomNumber(limit: number) {\n return Math.random() * limit;\n}\n")),(0,a.kt)("p",null,"Now, using the ",(0,a.kt)("inlineCode",{parentName:"p"},"getRandomNumber()")," method we previously created, we will add the 300 ",(0,a.kt)("strong",{parentName:"p"},(0,a.kt)("inlineCode",{parentName:"strong"},"cube"))," meshes to our scene at random positions. We'll add the cube to the scene and adjust its position between 0 and 10.\nAdditionally, we will add meshes to the ",(0,a.kt)("inlineCode",{parentName:"p"},"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 ",(0,a.kt)("inlineCode",{parentName:"p"},"add()")," method."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-js"},"function regenerateCubes() {\n for (let i = 0; i < 300; i++) {\n const cube = new THREE.Mesh(geometry, material);\n cube.position.x = getRandomNumber(10);\n cube.position.y = getRandomNumber(10);\n cube.position.z = getRandomNumber(10);\n cube.updateMatrix();\n world.scene.three.add(cube);\n culler.add(cube);\n cubes.push(cube);\n }\n}\n\nregenerateCubes();\n")),(0,a.kt)("h3",{id:"\ufe0f-updating-the-culler"},"\ud83d\udd04\ufe0f Updating the Culler"),(0,a.kt)("p",null,"Here comes the most crucial part! The core aim of ",(0,a.kt)("strong",{parentName:"p"},"ScreenCuller")," is to output just those components that are visible to the camera."),(0,a.kt)("admonition",{title:"How often should you update the culler?",type:"info"},(0,a.kt)("p",{parentName:"admonition"},"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.")),(0,a.kt)("p",null,"In this tutorial we are updating it each time the camera stops moving, which generally works well for most apps."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-js"},'culler.needsUpdate = true;\nworld.camera.controls.addEventListener("controlend", () => {\n culler.needsUpdate = true;\n});\n')),(0,a.kt)("h3",{id:"\ufe0f-measuring-the-performance-optional"},"\u23f1\ufe0f Measuring the performance (optional)"),(0,a.kt)("hr",null),(0,a.kt)("p",null,"We'll use the ",(0,a.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,a.kt)("pre",null,(0,a.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,a.kt)("p",null,"Great job! \ud83c\udf89 Now you know how to optimise your 3D scene using a\n",(0,a.kt)("strong",{parentName:"p"},"Screen Culler")," component! Your BIM app will now have unmatched performance and can render huge scenes easily. "),(0,a.kt)("iframe",{src:"https://thatopen.github.io/engine_components/examples/Cullers"}))}h.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/build/assets/js/2aba4d5d.5a51494f.js b/build/assets/js/2aba4d5d.5a51494f.js deleted file mode 100644 index b9c4935b0..000000000 --- a/build/assets/js/2aba4d5d.5a51494f.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkdocs=self.webpackChunkdocs||[]).push([[282],{3905:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>h});var r=n(7294);function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function l(e){for(var t=1;t=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var i=r.createContext({}),p=function(e){var t=r.useContext(i),n=t;return e&&(n="function"==typeof e?e(t):l(l({},t),e)),n},u=function(e){var t=p(e.components);return r.createElement(i.Provider,{value:t},e.children)},c="mdxType",m={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},d=r.forwardRef((function(e,t){var n=e.components,o=e.mdxType,a=e.originalType,i=e.parentName,u=s(e,["components","mdxType","originalType","parentName"]),c=p(n),d=o,h=c["".concat(i,".").concat(d)]||c[d]||m[d]||a;return n?r.createElement(h,l(l({ref:t},u),{},{components:n})):r.createElement(h,l({ref:t},u))}));function h(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var a=n.length,l=new Array(a);l[0]=d;var s={};for(var i in t)hasOwnProperty.call(t,i)&&(s[i]=t[i]);s.originalType=e,s[c]="string"==typeof e?e:o,l[1]=s;for(var p=2;p{n.r(t),n.d(t,{assets:()=>u,contentTitle:()=>i,default:()=>h,frontMatter:()=>s,metadata:()=>p,toc:()=>c});var r=n(7462),o=n(3366),a=(n(7294),n(3905)),l=["components"],s={},i=void 0,p={unversionedId:"Tutorials/Components/Core/Cullers",id:"Tutorials/Components/Core/Cullers",title:"Cullers",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/Cullers.mdx",sourceDirName:"Tutorials/Components/Core",slug:"/Tutorials/Components/Core/Cullers",permalink:"/Tutorials/Components/Core/Cullers",draft:!1,tags:[],version:"current",frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"Clipper",permalink:"/Tutorials/Components/Core/Clipper"},next:{title:"FragmentsManager",permalink:"/Tutorials/Components/Core/FragmentsManager"}},u={},c=[{value:"\ud83d\ude85 Managing Performance",id:"-managing-performance",level:3},{value:"\ud83c\udf0e Setting up a simple scene",id:"-setting-up-a-simple-scene",level:3},{value:"\ud83e\uddf0 Creating Screen Culler",id:"-creating-screen-culler",level:3},{value:"\ud83e\uddf1 Adding a ton of cubes",id:"-adding-a-ton-of-cubes",level:3},{value:"\ud83d\udd04\ufe0f Updating the Culler",id:"\ufe0f-updating-the-culler",level:3},{value:"\u23f1\ufe0f Measuring the performance (optional)",id:"\ufe0f-measuring-the-performance-optional",level:3}],m={toc:c},d="wrapper";function h(e){var t=e.components,n=(0,o.Z)(e,l);return(0,a.kt)(d,(0,r.Z)({},m,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("admonition",{title:"Source",type:"info"},(0,a.kt)("p",{parentName:"admonition"},"Copying and pasting? We've got you covered! You can find the full source code of this tutorial ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/ThatOpen/engine_components/blob/main/packages/core/src/core/Cullers/example.ts"},"here"),".")),(0,a.kt)("h3",{id:"-managing-performance"},"\ud83d\ude85 Managing Performance"),(0,a.kt)("hr",null),(0,a.kt)("p",null,"There are occasions when your scene has too many objects. Multiple objects being rendered simultaneously ",(0,a.kt)("strong",{parentName:"p"},"lengthens computation time"),"\u231b\ufe0f and ",(0,a.kt)("strong",{parentName:"p"},"degrades performance"),".\ud83c\udf21\ufe0f In this tutorial, we will use ",(0,a.kt)("strong",{parentName:"p"},"ScreenCuller")," to improve performance by reducing unnecessary computations.\ud83d\ude80"),(0,a.kt)("admonition",{title:'What\'s "culling"?',type:"tip"},(0,a.kt)("p",{parentName:"admonition"},"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.")),(0,a.kt)("p",null,"In this tutorial, we will import:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"Three.js")," to get some 3D entities for our app."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"@thatopen/components")," to set up the barebone of our app."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"Stats.js")," (optional) to measure the performance of our app.")),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-js"},'import * as THREE from "three";\nimport Stats from "stats.js";\nimport * as OBC from "../..";\n')),(0,a.kt)("h3",{id:"-setting-up-a-simple-scene"},"\ud83c\udf0e Setting up a simple scene"),(0,a.kt)("hr",null),(0,a.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,a.kt)("pre",null,(0,a.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);\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(13, 13, 13, 0, 0, 0);\n\nworld.scene.setup();\n\nconst grids = components.get(OBC.Grids);\ngrids.create(world);\n')),(0,a.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,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-js"},"world.scene.three.background = null;\n")),(0,a.kt)("h3",{id:"-creating-screen-culler"},"\ud83e\uddf0 Creating Screen Culler"),(0,a.kt)("hr",null),(0,a.kt)("p",null,"Although adding Screen Culler to your project can appear difficult, it is actually very easy. We just need to get the ",(0,a.kt)("inlineCode",{parentName:"p"},"Cullers")," component and create a new instance of ",(0,a.kt)("inlineCode",{parentName:"p"},"ScreenCuller"),". Remember that although you can instance the Cullers component, it's better to get it from the ",(0,a.kt)("inlineCode",{parentName:"p"},"components")," object, as all the components are meant to be singletons within a Component instance, and this ensures that."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-js"},"const cullers = components.get(OBC.Cullers);\nconst culler = cullers.create(world);\n")),(0,a.kt)("p",null,"You can use the ",(0,a.kt)("inlineCode",{parentName:"p"},"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:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-js"},"culler.threshold = 200;\n")),(0,a.kt)("p",null,"Additionally, we will activate the ",(0,a.kt)("inlineCode",{parentName:"p"},"culler.renderDebugFrame")," so that we can see the 2D screen of the elements that are not occluded. We will get the ",(0,a.kt)("strong",{parentName:"p"},"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 ",(0,a.kt)("inlineCode",{parentName:"p"},'debugFrame.style.visibility = "collapse";')," line."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-js"},'culler.renderDebugFrame = true;\nconst debugFrame = culler.renderer.domElement;\ndocument.body.appendChild(debugFrame);\ndebugFrame.style.position = "fixed";\ndebugFrame.style.left = "0";\ndebugFrame.style.bottom = "0";\ndebugFrame.style.visibility = "collapse";\n')),(0,a.kt)("h3",{id:"-adding-a-ton-of-cubes"},"\ud83e\uddf1 Adding a ton of cubes"),(0,a.kt)("p",null,"We'll add the Simple 3D Cube and do it ",(0,a.kt)("strong",{parentName:"p"},"300 times"),"!\ud83e\udd2f We'll generate box geometry and use Lambert material."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-js"},'const cubes: THREE.Mesh[] = [];\nconst geometry = new THREE.BoxGeometry(2, 2, 2);\nconst material = new THREE.MeshLambertMaterial({ color: "#6528D7" });\n')),(0,a.kt)("admonition",{title:"Randomising the Cubes Placement",type:"info"},(0,a.kt)("p",{parentName:"admonition"},"We'll write a quick ",(0,a.kt)("strong",{parentName:"p"},"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.\ud83d\udccc")),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-js"},"function getRandomNumber(limit: number) {\n return Math.random() * limit;\n}\n")),(0,a.kt)("p",null,"Now, using the ",(0,a.kt)("inlineCode",{parentName:"p"},"getRandomNumber()")," method we previously created, we will add the 300 ",(0,a.kt)("strong",{parentName:"p"},(0,a.kt)("inlineCode",{parentName:"strong"},"cube"))," meshes to our scene at random positions. We'll add the cube to the scene and adjust its position between 0 and 10.\nAdditionally, we will add meshes to the ",(0,a.kt)("inlineCode",{parentName:"p"},"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 ",(0,a.kt)("inlineCode",{parentName:"p"},"add()")," method."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-js"},"function regenerateCubes() {\n for (let i = 0; i < 300; i++) {\n const cube = new THREE.Mesh(geometry, material);\n cube.position.x = getRandomNumber(10);\n cube.position.y = getRandomNumber(10);\n cube.position.z = getRandomNumber(10);\n cube.updateMatrix();\n world.scene.three.add(cube);\n culler.add(cube);\n cubes.push(cube);\n }\n}\n\nregenerateCubes();\n")),(0,a.kt)("h3",{id:"\ufe0f-updating-the-culler"},"\ud83d\udd04\ufe0f Updating the Culler"),(0,a.kt)("p",null,"Here comes the most crucial part! The core aim of ",(0,a.kt)("strong",{parentName:"p"},"ScreenCuller")," is to output just those components that are visible to the camera."),(0,a.kt)("admonition",{title:"How often should you update the culler?",type:"info"},(0,a.kt)("p",{parentName:"admonition"},"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.")),(0,a.kt)("p",null,"In this tutorial we are updating it each time the camera stops moving, which generally works well for most apps."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-js"},'culler.needsUpdate = true;\nworld.camera.controls.addEventListener("controlend", () => {\n culler.needsUpdate = true;\n});\n')),(0,a.kt)("h3",{id:"\ufe0f-measuring-the-performance-optional"},"\u23f1\ufe0f Measuring the performance (optional)"),(0,a.kt)("hr",null),(0,a.kt)("p",null,"We'll use the ",(0,a.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,a.kt)("pre",null,(0,a.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,a.kt)("p",null,"Great job! \ud83c\udf89 Now you know how to optimise your 3D scene using a\n",(0,a.kt)("strong",{parentName:"p"},"Screen Culler")," component! Your BIM app will now have unmatched performance and can render huge scenes easily. "),(0,a.kt)("iframe",{src:"https://thatopen.github.io/engine_components/examples/Cullers"}))}h.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/build/assets/js/35bc646f.a79cdf7e.js b/build/assets/js/35bc646f.a79cdf7e.js new file mode 100644 index 000000000..6e5b94e82 --- /dev/null +++ b/build/assets/js/35bc646f.a79cdf7e.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkdocs=self.webpackChunkdocs||[]).push([[6635],{3905:(e,t,n)=>{n.d(t,{Zo:()=>c,kt:()=>h});var a=n(7294);function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function l(e){for(var t=1;t=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var p=a.createContext({}),s=function(e){var t=a.useContext(p),n=t;return e&&(n="function"==typeof e?e(t):l(l({},t),e)),n},c=function(e){var t=s(e.components);return a.createElement(p.Provider,{value:t},e.children)},u="mdxType",m={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},d=a.forwardRef((function(e,t){var n=e.components,o=e.mdxType,i=e.originalType,p=e.parentName,c=r(e,["components","mdxType","originalType","parentName"]),u=s(n),d=o,h=u["".concat(p,".").concat(d)]||u[d]||m[d]||i;return n?a.createElement(h,l(l({ref:t},c),{},{components:n})):a.createElement(h,l({ref:t},c))}));function h(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var i=n.length,l=new Array(i);l[0]=d;var r={};for(var p in t)hasOwnProperty.call(t,p)&&(r[p]=t[p]);r.originalType=e,r[u]="string"==typeof e?e:o,l[1]=r;for(var s=2;s{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>p,default:()=>h,frontMatter:()=>r,metadata:()=>s,toc:()=>u});var a=n(7462),o=n(3366),i=(n(7294),n(3905)),l=["components"],r={},p=void 0,s={unversionedId:"Tutorials/Components/Core/Clipper",id:"Tutorials/Components/Core/Clipper",title:"Clipper",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/Clipper.mdx",sourceDirName:"Tutorials/Components/Core",slug:"/Tutorials/Components/Core/Clipper",permalink:"/Tutorials/Components/Core/Clipper",draft:!1,tags:[],version:"current",frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"Classifier",permalink:"/Tutorials/Components/Core/Classifier"},next:{title:"Cullers",permalink:"/Tutorials/Components/Core/Cullers"}},c={},u=[{value:"\u2702\ufe0f Cutting our scene with planes",id:"\ufe0f-cutting-our-scene-with-planes",level:3},{value:"\ud83c\udf0e Setting up a simple scene",id:"-setting-up-a-simple-scene",level:3},{value:"\ud83c\udfb2 Creating a Cube Mesh",id:"-creating-a-cube-mesh",level:3},{value:"\u26a1 Initializing the Raycaster",id:"-initializing-the-raycaster",level:3},{value:"\u2699\ufe0f Adding the Clipper",id:"\ufe0f-adding-the-clipper",level:3},{value:"\ud83e\uddf9 Deleting the Clipping Planes",id:"-deleting-the-clipping-planes",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:u},d="wrapper";function h(e){var t=e.components,n=(0,o.Z)(e,l);return(0,i.kt)(d,(0,a.Z)({},m,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("admonition",{title:"Source",type:"info"},(0,i.kt)("p",{parentName:"admonition"},"Copying and pasting? We've got you covered! You can find the full source code of this tutorial ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/ThatOpen/engine_components/blob/main/packages/core/src/core/Clipper/example.ts"},"here"),".")),(0,i.kt)("h3",{id:"\ufe0f-cutting-our-scene-with-planes"},"\u2702\ufe0f Cutting our scene with planes"),(0,i.kt)("hr",null),(0,i.kt)("p",null,"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.\ud83d\udcaa"),(0,i.kt)("admonition",{title:"Clipping?",type:"tip"},(0,i.kt)("p",{parentName:"admonition"},'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.')),(0,i.kt)("p",null,"In this tutorial, we will import:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"Three.js")," to get some 3D entities for our app."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"@thatopen/components")," to set up the barebone of our app."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"@thatopen/ui")," to add some simple and cool UI menus."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"Stats.js")," (optional) to measure the performance of our app.")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-js"},'import * as THREE from "three";\nimport Stats from "stats.js";\nimport * as BUI from "@thatopen/ui";\nimport * as OBC from "@thatopen/components";\n')),(0,i.kt)("h3",{id:"-setting-up-a-simple-scene"},"\ud83c\udf0e Setting up a simple scene"),(0,i.kt)("hr",null),(0,i.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,i.kt)("pre",null,(0,i.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);\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(10, 10, 10, 0, 0, 0);\n\nworld.scene.setup();\n')),(0,i.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,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-js"},"world.scene.three.background = null;\n")),(0,i.kt)("h3",{id:"-creating-a-cube-mesh"},"\ud83c\udfb2 Creating a Cube Mesh"),(0,i.kt)("hr",null),(0,i.kt)("p",null,"Let's start by adding a Cube, which we can dissect. We will create a ",(0,i.kt)("a",{parentName:"p",href:"https://threejs.org/docs/index.html?q=box#api/en/geometries/BoxGeometry"},"Cube")," with ",(0,i.kt)("inlineCode",{parentName:"p"},"3x3x3")," dimensions and use purple color for the material."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-js"},'const cubeGeometry = new THREE.BoxGeometry(3, 3, 3);\nconst cubeMaterial = new THREE.MeshStandardMaterial({ color: "#6528D7" });\nconst cube = new THREE.Mesh(cubeGeometry, cubeMaterial);\ncube.position.set(0, 1.5, 0);\nworld.scene.three.add(cube);\nworld.meshes.add(cube);\n')),(0,i.kt)("h3",{id:"-initializing-the-raycaster"},"\u26a1 Initializing the Raycaster"),(0,i.kt)("hr",null),(0,i.kt)("p",null,"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."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-js"},"const casters = components.get(OBC.Raycasters);\ncasters.get(world);\n")),(0,i.kt)("h3",{id:"\ufe0f-adding-the-clipper"},"\u2699\ufe0f Adding the Clipper"),(0,i.kt)("hr",null),(0,i.kt)("p",null,"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 ",(0,i.kt)("inlineCode",{parentName:"p"},"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."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-js"},"const clipper = components.get(OBC.Clipper);\n")),(0,i.kt)("admonition",{title:"Controllign the plane",type:"info"},(0,i.kt)("p",{parentName:"admonition"},"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 ",(0,i.kt)("inlineCode",{parentName:"p"},"plane.enabled")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"plane.visible"),". To control the activation and visibility of all planes, use ",(0,i.kt)("inlineCode",{parentName:"p"},"clipper.enabled")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"clipper.visible"),"."),(0,i.kt)("pre",{parentName:"admonition"},(0,i.kt)("code",{parentName:"pre",className:"language-js"},"clipper.enabled = true;\n")),(0,i.kt)("h3",{parentName:"admonition",id:"-performing-clipping-events"},"\ud83e\udd1d Performing Clipping Events"),(0,i.kt)("hr",{parentName:"admonition"}),(0,i.kt)("p",{parentName:"admonition"},"Now, we want a way to create a clipping plane on demand. You can do it with a ",(0,i.kt)("inlineCode",{parentName:"p"},"Single Click")," or ",(0,i.kt)("inlineCode",{parentName:"p"},"Double Click")," of a mouse. For this tutorial, we will use ",(0,i.kt)("inlineCode",{parentName:"p"},"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."),(0,i.kt)("pre",{parentName:"admonition"},(0,i.kt)("code",{parentName:"pre",className:"language-js"},"container.ondblclick = () => clipper.create(world);\n"))),(0,i.kt)("p",null,"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. \ud83d\ude0e\n:::"),(0,i.kt)("h3",{id:"-deleting-the-clipping-planes"},"\ud83e\uddf9 Deleting the Clipping Planes"),(0,i.kt)("hr",null),(0,i.kt)("p",null,"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 ",(0,i.kt)("inlineCode",{parentName:"p"},"clipper.delete(world)")," (which will pick the first plane under the mouse using the raycaster in the specified world) or ",(0,i.kt)("inlineCode",{parentName:"p"},"clipper.delete(world, plane)")," (which will delete a specific clipping plane)."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-js"},'window.onkeydown = (event) => {\n if (event.code === "Delete" || event.code === "Backspace") {\n clipper.delete(world);\n }\n};\n')),(0,i.kt)("admonition",{title:"Delete all the Clipping Planes",type:"tip"},(0,i.kt)("p",{parentName:"admonition"},"\u274e If you want to safely delete all the clipping planes that were created you can simply call ",(0,i.kt)("inlineCode",{parentName:"p"},"clipper.deleteAll()"),".")),(0,i.kt)("h3",{id:"\ufe0f-measuring-the-performance-optional"},"\u23f1\ufe0f Measuring the performance (optional)"),(0,i.kt)("hr",null),(0,i.kt)("p",null,"We'll use the ",(0,i.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,i.kt)("pre",null,(0,i.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,i.kt)("h3",{id:"-adding-some-ui"},"\ud83e\udde9 Adding some UI"),(0,i.kt)("hr",null),(0,i.kt)("p",null,"We will use the ",(0,i.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,i.kt)("inlineCode",{parentName:"p"},"init")," method of the ",(0,i.kt)("inlineCode",{parentName:"p"},"BUI.Manager")," class to initialize the library:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-js"},"BUI.Manager.init();\n")),(0,i.kt)("p",null,"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!"),(0,i.kt)("pre",null,(0,i.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 \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n `;\n});\n\ndocument.body.append(panel);\n')),(0,i.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,i.kt)("pre",null,(0,i.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,i.kt)("h3",{id:"-wrap-up"},"\ud83c\udf89 Wrap up"),(0,i.kt)("hr",null),(0,i.kt)("p",null,"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."),(0,i.kt)("iframe",{src:"https://thatopen.github.io/engine_components/examples/Clipper"}))}h.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/build/assets/js/35bc646f.ed031adf.js b/build/assets/js/35bc646f.ed031adf.js deleted file mode 100644 index 544dc494b..000000000 --- a/build/assets/js/35bc646f.ed031adf.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkdocs=self.webpackChunkdocs||[]).push([[6635],{3905:(e,t,n)=>{n.d(t,{Zo:()=>c,kt:()=>h});var a=n(7294);function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function l(e){for(var t=1;t=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var p=a.createContext({}),s=function(e){var t=a.useContext(p),n=t;return e&&(n="function"==typeof e?e(t):l(l({},t),e)),n},c=function(e){var t=s(e.components);return a.createElement(p.Provider,{value:t},e.children)},u="mdxType",m={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},d=a.forwardRef((function(e,t){var n=e.components,o=e.mdxType,i=e.originalType,p=e.parentName,c=r(e,["components","mdxType","originalType","parentName"]),u=s(n),d=o,h=u["".concat(p,".").concat(d)]||u[d]||m[d]||i;return n?a.createElement(h,l(l({ref:t},c),{},{components:n})):a.createElement(h,l({ref:t},c))}));function h(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var i=n.length,l=new Array(i);l[0]=d;var r={};for(var p in t)hasOwnProperty.call(t,p)&&(r[p]=t[p]);r.originalType=e,r[u]="string"==typeof e?e:o,l[1]=r;for(var s=2;s{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>p,default:()=>h,frontMatter:()=>r,metadata:()=>s,toc:()=>u});var a=n(7462),o=n(3366),i=(n(7294),n(3905)),l=["components"],r={},p=void 0,s={unversionedId:"Tutorials/Components/Core/Clipper",id:"Tutorials/Components/Core/Clipper",title:"Clipper",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/Clipper.mdx",sourceDirName:"Tutorials/Components/Core",slug:"/Tutorials/Components/Core/Clipper",permalink:"/Tutorials/Components/Core/Clipper",draft:!1,tags:[],version:"current",frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"BoundingBoxer",permalink:"/Tutorials/Components/Core/BoundingBoxer"},next:{title:"Cullers",permalink:"/Tutorials/Components/Core/Cullers"}},c={},u=[{value:"\u2702\ufe0f Cutting our scene with planes",id:"\ufe0f-cutting-our-scene-with-planes",level:3},{value:"\ud83c\udf0e Setting up a simple scene",id:"-setting-up-a-simple-scene",level:3},{value:"\ud83c\udfb2 Creating a Cube Mesh",id:"-creating-a-cube-mesh",level:3},{value:"\u26a1 Initializing the Raycaster",id:"-initializing-the-raycaster",level:3},{value:"\u2699\ufe0f Adding the Clipper",id:"\ufe0f-adding-the-clipper",level:3},{value:"\ud83e\uddf9 Deleting the Clipping Planes",id:"-deleting-the-clipping-planes",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:u},d="wrapper";function h(e){var t=e.components,n=(0,o.Z)(e,l);return(0,i.kt)(d,(0,a.Z)({},m,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("admonition",{title:"Source",type:"info"},(0,i.kt)("p",{parentName:"admonition"},"Copying and pasting? We've got you covered! You can find the full source code of this tutorial ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/ThatOpen/engine_components/blob/main/packages/core/src/core/Clipper/example.ts"},"here"),".")),(0,i.kt)("h3",{id:"\ufe0f-cutting-our-scene-with-planes"},"\u2702\ufe0f Cutting our scene with planes"),(0,i.kt)("hr",null),(0,i.kt)("p",null,"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.\ud83d\udcaa"),(0,i.kt)("admonition",{title:"Clipping?",type:"tip"},(0,i.kt)("p",{parentName:"admonition"},'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.')),(0,i.kt)("p",null,"In this tutorial, we will import:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"Three.js")," to get some 3D entities for our app."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"@thatopen/components")," to set up the barebone of our app."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"@thatopen/ui")," to add some simple and cool UI menus."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"Stats.js")," (optional) to measure the performance of our app.")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-js"},'import * as THREE from "three";\nimport Stats from "stats.js";\nimport * as BUI from "@thatopen/ui";\nimport * as OBC from "@thatopen/components";\n')),(0,i.kt)("h3",{id:"-setting-up-a-simple-scene"},"\ud83c\udf0e Setting up a simple scene"),(0,i.kt)("hr",null),(0,i.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,i.kt)("pre",null,(0,i.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);\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(10, 10, 10, 0, 0, 0);\n\nworld.scene.setup();\n')),(0,i.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,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-js"},"world.scene.three.background = null;\n")),(0,i.kt)("h3",{id:"-creating-a-cube-mesh"},"\ud83c\udfb2 Creating a Cube Mesh"),(0,i.kt)("hr",null),(0,i.kt)("p",null,"Let's start by adding a Cube, which we can dissect. We will create a ",(0,i.kt)("a",{parentName:"p",href:"https://threejs.org/docs/index.html?q=box#api/en/geometries/BoxGeometry"},"Cube")," with ",(0,i.kt)("inlineCode",{parentName:"p"},"3x3x3")," dimensions and use purple color for the material."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-js"},'const cubeGeometry = new THREE.BoxGeometry(3, 3, 3);\nconst cubeMaterial = new THREE.MeshStandardMaterial({ color: "#6528D7" });\nconst cube = new THREE.Mesh(cubeGeometry, cubeMaterial);\ncube.position.set(0, 1.5, 0);\nworld.scene.three.add(cube);\nworld.meshes.add(cube);\n')),(0,i.kt)("h3",{id:"-initializing-the-raycaster"},"\u26a1 Initializing the Raycaster"),(0,i.kt)("hr",null),(0,i.kt)("p",null,"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."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-js"},"const casters = components.get(OBC.Raycasters);\ncasters.get(world);\n")),(0,i.kt)("h3",{id:"\ufe0f-adding-the-clipper"},"\u2699\ufe0f Adding the Clipper"),(0,i.kt)("hr",null),(0,i.kt)("p",null,"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 ",(0,i.kt)("inlineCode",{parentName:"p"},"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."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-js"},"const clipper = components.get(OBC.Clipper);\n")),(0,i.kt)("admonition",{title:"Controllign the plane",type:"info"},(0,i.kt)("p",{parentName:"admonition"},"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 ",(0,i.kt)("inlineCode",{parentName:"p"},"plane.enabled")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"plane.visible"),". To control the activation and visibility of all planes, use ",(0,i.kt)("inlineCode",{parentName:"p"},"clipper.enabled")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"clipper.visible"),"."),(0,i.kt)("pre",{parentName:"admonition"},(0,i.kt)("code",{parentName:"pre",className:"language-js"},"clipper.enabled = true;\n")),(0,i.kt)("h3",{parentName:"admonition",id:"-performing-clipping-events"},"\ud83e\udd1d Performing Clipping Events"),(0,i.kt)("hr",{parentName:"admonition"}),(0,i.kt)("p",{parentName:"admonition"},"Now, we want a way to create a clipping plane on demand. You can do it with a ",(0,i.kt)("inlineCode",{parentName:"p"},"Single Click")," or ",(0,i.kt)("inlineCode",{parentName:"p"},"Double Click")," of a mouse. For this tutorial, we will use ",(0,i.kt)("inlineCode",{parentName:"p"},"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."),(0,i.kt)("pre",{parentName:"admonition"},(0,i.kt)("code",{parentName:"pre",className:"language-js"},"container.ondblclick = () => clipper.create(world);\n"))),(0,i.kt)("p",null,"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. \ud83d\ude0e\n:::"),(0,i.kt)("h3",{id:"-deleting-the-clipping-planes"},"\ud83e\uddf9 Deleting the Clipping Planes"),(0,i.kt)("hr",null),(0,i.kt)("p",null,"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 ",(0,i.kt)("inlineCode",{parentName:"p"},"clipper.delete(world)")," (which will pick the first plane under the mouse using the raycaster in the specified world) or ",(0,i.kt)("inlineCode",{parentName:"p"},"clipper.delete(world, plane)")," (which will delete a specific clipping plane)."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-js"},'window.onkeydown = (event) => {\n if (event.code === "Delete" || event.code === "Backspace") {\n clipper.delete(world);\n }\n};\n')),(0,i.kt)("admonition",{title:"Delete all the Clipping Planes",type:"tip"},(0,i.kt)("p",{parentName:"admonition"},"\u274e If you want to safely delete all the clipping planes that were created you can simply call ",(0,i.kt)("inlineCode",{parentName:"p"},"clipper.deleteAll()"),".")),(0,i.kt)("h3",{id:"\ufe0f-measuring-the-performance-optional"},"\u23f1\ufe0f Measuring the performance (optional)"),(0,i.kt)("hr",null),(0,i.kt)("p",null,"We'll use the ",(0,i.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,i.kt)("pre",null,(0,i.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,i.kt)("h3",{id:"-adding-some-ui"},"\ud83e\udde9 Adding some UI"),(0,i.kt)("hr",null),(0,i.kt)("p",null,"We will use the ",(0,i.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,i.kt)("inlineCode",{parentName:"p"},"init")," method of the ",(0,i.kt)("inlineCode",{parentName:"p"},"BUI.Manager")," class to initialize the library:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-js"},"BUI.Manager.init();\n")),(0,i.kt)("p",null,"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!"),(0,i.kt)("pre",null,(0,i.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 \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n `;\n});\n\ndocument.body.append(panel);\n')),(0,i.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,i.kt)("pre",null,(0,i.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,i.kt)("h3",{id:"-wrap-up"},"\ud83c\udf89 Wrap up"),(0,i.kt)("hr",null),(0,i.kt)("p",null,"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."),(0,i.kt)("iframe",{src:"https://thatopen.github.io/engine_components/examples/Clipper"}))}h.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/build/assets/js/4c4c2199.63b1a199.js b/build/assets/js/4c4c2199.63b1a199.js new file mode 100644 index 000000000..af170a146 --- /dev/null +++ b/build/assets/js/4c4c2199.63b1a199.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkdocs=self.webpackChunkdocs||[]).push([[549],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>f});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 l(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 r(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(o=0;o=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var s=o.createContext({}),c=function(e){var t=o.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):r(r({},t),e)),n},p=function(e){var t=c(e.components);return o.createElement(s.Provider,{value:t},e.children)},m="mdxType",u={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},d=o.forwardRef((function(e,t){var n=e.components,a=e.mdxType,l=e.originalType,s=e.parentName,p=i(e,["components","mdxType","originalType","parentName"]),m=c(n),d=a,f=m["".concat(s,".").concat(d)]||m[d]||u[d]||l;return n?o.createElement(f,r(r({ref:t},p),{},{components:n})):o.createElement(f,r({ref:t},p))}));function f(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var l=n.length,r=new Array(l);r[0]=d;var i={};for(var s in t)hasOwnProperty.call(t,s)&&(i[s]=t[s]);i.originalType=e,i[m]="string"==typeof e?e:a,r[1]=i;for(var c=2;c{n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>s,default:()=>f,frontMatter:()=>i,metadata:()=>c,toc:()=>m});var o=n(7462),a=n(3366),l=(n(7294),n(3905)),r=["components"],i={},s=void 0,c={unversionedId:"Tutorials/Components/Core/Classifier",id:"Tutorials/Components/Core/Classifier",title:"Classifier",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/Classifier.mdx",sourceDirName:"Tutorials/Components/Core",slug:"/Tutorials/Components/Core/Classifier",permalink:"/Tutorials/Components/Core/Classifier",draft:!1,tags:[],version:"current",frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"BoundingBoxer",permalink:"/Tutorials/Components/Core/BoundingBoxer"},next:{title:"Clipper",permalink:"/Tutorials/Components/Core/Clipper"}},p={},m=[{value:"\ud83d\udd34\ud83d\udd35 Classifying your BIM models",id:"-classifying-your-bim-models",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\uddc3\ufe0f Classifiying the BIM model",id:"\ufe0f-classifiying-the-bim-model",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}],u={toc:m},d="wrapper";function f(e){var t=e.components,n=(0,a.Z)(e,r);return(0,l.kt)(d,(0,o.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,l.kt)("admonition",{title:"Source",type:"info"},(0,l.kt)("p",{parentName:"admonition"},"Copying and pasting? We've got you covered! You can find the full source code of this tutorial ",(0,l.kt)("a",{parentName:"p",href:"https://github.com/ThatOpen/engine_components/blob/main/packages/core/src/fragments/Classifier/example.ts"},"here"),".")),(0,l.kt)("h3",{id:"-classifying-your-bim-models"},"\ud83d\udd34\ud83d\udd35 Classifying your BIM models"),(0,l.kt)("hr",null),(0,l.kt)("p",null,"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."),(0,l.kt)("admonition",{title:"Why classifications?",type:"tip"},(0,l.kt)("p",{parentName:"admonition"},"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.")),(0,l.kt)("p",null,"In this tutorial, we will import:"),(0,l.kt)("ul",null,(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("inlineCode",{parentName:"li"},"Three.js")," to get some 3D entities for our app."),(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("inlineCode",{parentName:"li"},"web-ifc")," to get some IFC items."),(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("inlineCode",{parentName:"li"},"@thatopen/ui")," to add some simple and cool UI menus."),(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("inlineCode",{parentName:"li"},"@thatopen/components")," to set up the barebone of our app."),(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("inlineCode",{parentName:"li"},"Stats.js")," (optional) to measure the performance of our app.")),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-js"},'import Stats from "stats.js";\nimport * as THREE from "three";\nimport * as BUI from "@thatopen/ui";\nimport * as WEBIFC from "web-ifc";\nimport * as OBC from "@thatopen/components";\n')),(0,l.kt)("h3",{id:"-setting-up-a-simple-scene"},"\ud83c\udf0e Setting up a simple scene"),(0,l.kt)("hr",null),(0,l.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,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-js"},'const container = document.getElementById("container")!;\nconst components = new OBC.Components();\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,l.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,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-js"},"world.scene.three.background = null;\n")),(0,l.kt)("h3",{id:"-loading-a-bim-model"},"\ud83e\uddf3 Loading a BIM model"),(0,l.kt)("hr",null),(0,l.kt)("p",null,"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."),(0,l.kt)("admonition",{title:"Fragments?",type:"tip"},(0,l.kt)("p",{parentName:"admonition"},"If you are not familiar with fragments, check out the IfcLoader tutorial!")),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-js"},'const fragments = new OBC.FragmentsManager(components);\nconst file = await fetch("https://thatopen.github.io/engine_components/resources/small.frag");\nconst data = await file.arrayBuffer();\nconst buffer = new Uint8Array(data);\nconst model = fragments.load(buffer);\nworld.scene.three.add(model);\n')),(0,l.kt)("h3",{id:"\ufe0f-classifiying-the-bim-model"},"\ud83d\uddc3\ufe0f Classifiying the BIM model"),(0,l.kt)("hr",null),(0,l.kt)("p",null,"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."),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-js"},"const classifier = components.get(OBC.Classifier);\n")),(0,l.kt)("p",null,"Now we can classify the BIM model. The classifier includes 3 methods:"),(0,l.kt)("ul",null,(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("inlineCode",{parentName:"li"},"byEntity"),": classifies the model by IFC category."),(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("inlineCode",{parentName:"li"},"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)."),(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("inlineCode",{parentName:"li"},"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.")),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-js"},'classifier.byEntity(model);\nclassifier.byIfcRel(model, WEBIFC.IFCRELCONTAINEDINSPATIALSTRUCTURE, "storeys");\nclassifier.byModel(model.uuid, model);\n')),(0,l.kt)("p",null,"Now, to get the fragments set that belong to a certain classification, we can use the ",(0,l.kt)("inlineCode",{parentName:"p"},"find()"),' method. This method allows us to pass an object with filters. For example, to get all items of category "IFCWALLSTANDARDCASE", we can do: '),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-js"},'const walls = classifier.find({\n entities: ["IFCWALLSTANDARDCASE"],\n});\n')),(0,l.kt)("p",null,"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:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-js"},'const slabs = classifier.find({\n entities: ["IFCSLAB"],\n});\n\nconst curtainWalls = classifier.find({\n entities: ["IFCMEMBER", "IFCPLATE"],\n});\n\nconst furniture = classifier.find({\n entities: ["IFCFURNISHINGELEMENT"],\n});\n\nconst doors = classifier.find({\n entities: ["IFCDOOR"],\n});\n\nconst all = classifier.find({\n models: [model.uuid],\n});\n')),(0,l.kt)("h3",{id:"\ufe0f-measuring-the-performance-optional"},"\u23f1\ufe0f Measuring the performance (optional)"),(0,l.kt)("hr",null),(0,l.kt)("p",null,"We'll use the ",(0,l.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,l.kt)("pre",null,(0,l.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,l.kt)("h3",{id:"-adding-some-ui"},"\ud83e\udde9 Adding some UI"),(0,l.kt)("hr",null),(0,l.kt)("p",null,"We will use the ",(0,l.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,l.kt)("inlineCode",{parentName:"p"},"init")," method of the ",(0,l.kt)("inlineCode",{parentName:"p"},"BUI.Manager")," class to initialize the library:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-js"},"BUI.Manager.init();\n")),(0,l.kt)("p",null,"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!"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-js"},'const color = new THREE.Color();\n\nconst panel = BUI.Component.create(() => {\n return BUI.html`\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n \n \n `;\n});\n\ndocument.body.append(panel);\n')),(0,l.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,l.kt)("pre",null,(0,l.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,l.kt)("h3",{id:"-wrap-up"},"\ud83c\udf89 Wrap up"),(0,l.kt)("hr",null),(0,l.kt)("p",null,"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."),(0,l.kt)("iframe",{src:"https://thatopen.github.io/engine_components/examples/Classifier"}))}f.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/build/assets/js/5c7b714e.24dcdc83.js b/build/assets/js/5c7b714e.24dcdc83.js deleted file mode 100644 index 267f1ee1b..000000000 --- a/build/assets/js/5c7b714e.24dcdc83.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkdocs=self.webpackChunkdocs||[]).push([[4283],{3905:(e,n,t)=>{t.d(n,{Zo:()=>m,kt:()=>d});var a=t(7294);function r(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function o(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);n&&(a=a.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,a)}return t}function i(e){for(var n=1;n=0||(r[t]=e[t]);return r}(e,n);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(r[t]=e[t])}return r}var l=a.createContext({}),p=function(e){var n=a.useContext(l),t=n;return e&&(t="function"==typeof e?e(n):i(i({},n),e)),t},m=function(e){var n=p(e.components);return a.createElement(l.Provider,{value:n},e.children)},c="mdxType",u={inlineCode:"code",wrapper:function(e){var n=e.children;return a.createElement(a.Fragment,{},n)}},g=a.forwardRef((function(e,n){var t=e.components,r=e.mdxType,o=e.originalType,l=e.parentName,m=s(e,["components","mdxType","originalType","parentName"]),c=p(t),g=r,d=c["".concat(l,".").concat(g)]||c[g]||u[g]||o;return t?a.createElement(d,i(i({ref:n},m),{},{components:t})):a.createElement(d,i({ref:n},m))}));function d(e,n){var t=arguments,r=n&&n.mdxType;if("string"==typeof e||r){var o=t.length,i=new Array(o);i[0]=g;var s={};for(var l in n)hasOwnProperty.call(n,l)&&(s[l]=n[l]);s.originalType=e,s[c]="string"==typeof e?e:r,i[1]=s;for(var p=2;p{t.r(n),t.d(n,{assets:()=>m,contentTitle:()=>l,default:()=>d,frontMatter:()=>s,metadata:()=>p,toc:()=>c});var a=t(7462),r=t(3366),o=(t(7294),t(3905)),i=["components"],s={},l=void 0,p={unversionedId:"Tutorials/Components/Core/FragmentsManager",id:"Tutorials/Components/Core/FragmentsManager",title:"FragmentsManager",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/FragmentsManager.mdx",sourceDirName:"Tutorials/Components/Core",slug:"/Tutorials/Components/Core/FragmentsManager",permalink:"/Tutorials/Components/Core/FragmentsManager",draft:!1,tags:[],version:"current",frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"Cullers",permalink:"/Tutorials/Components/Core/Cullers"},next:{title:"Grids",permalink:"/Tutorials/Components/Core/Grids"}},m={},c=[{value:"\ud83e\uddf6 Managing Fragment Efficiently",id:"-managing-fragment-efficiently",level:3},{value:"\ud83e\udded Fragment Powerhouse",id:"-fragment-powerhouse",level:3},{value:"\ud83e\udde9 Add Fragment To Scene",id:"-add-fragment-to-scene",level:3},{value:"\ud83d\udce4 Storing Fragment",id:"-storing-fragment",level:3},{value:"\ud83e\uddf9 Discard Fragment and Clean the Scene",id:"-discard-fragment-and-clean-the-scene",level:3}],u={toc:c},g="wrapper";function d(e){var n=e.components,t=(0,r.Z)(e,i);return(0,o.kt)(g,(0,a.Z)({},u,t,{components:n,mdxType:"MDXLayout"}),(0,o.kt)("admonition",{title:"Source",type:"info"},(0,o.kt)("p",{parentName:"admonition"},"Copying and pasting? We've got you covered! You can find the full source code of this tutorial ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/ThatOpen/engine_components/blob/main/packages/core/src/fragments/FragmentsManager/example.ts"},"here"),".")),(0,o.kt)("h3",{id:"-managing-fragment-efficiently"},"\ud83e\uddf6 Managing Fragment Efficiently"),(0,o.kt)("hr",null),(0,o.kt)("p",null,"Until recently, we had been adding 3D objects to the ",(0,o.kt)("strong",{parentName:"p"},"Scene")," with the traditional ",(0,o.kt)("inlineCode",{parentName:"p"},"scene.add")," API.\nWe will now use ",(0,o.kt)("strong",{parentName:"p"},(0,o.kt)("a",{parentName:"strong",href:"https://github.com/ThatOpen/engine_fragment"},"Fragment"))," to work with large BIM models.\ud83c\udfd7\ufe0f\nFragment are faster and easier to use Geometric API, which reduces draw calls and speeds up the processing of 3D objects.\nIn this tutorial, we will load and render ",(0,o.kt)("inlineCode",{parentName:"p"},".frag")," files using ",(0,o.kt)("strong",{parentName:"p"},"Fragment Manager"),"."),(0,o.kt)("admonition",{title:"First, let's set up a simple scene!",type:"tip"},(0,o.kt)("p",{parentName:"admonition"},"\ud83d\udc40 If you haven't started there, check out ",(0,o.kt)("a",{parentName:"p",href:"SimpleScene.mdx"},"that tutorial first"),"!")),(0,o.kt)("h3",{id:"-fragment-powerhouse"},"\ud83e\udded Fragment Powerhouse"),(0,o.kt)("hr",null),(0,o.kt)("p",null,"Let's begin by including ",(0,o.kt)("a",{parentName:"p",href:"../api/classes/components.FragmentManager"},"Fragment Manager"),",\nwhich requires the parameter ",(0,o.kt)("inlineCode",{parentName:"p"},"component")," to be provided to it.\nIn terms of capabilities, Fragment Manager is a true powerhouse.\ud83c\udfed"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},"const fragments = new OBC.FragmentsManager(components);\n")),(0,o.kt)("h3",{id:"-add-fragment-to-scene"},"\ud83e\udde9 Add Fragment To Scene"),(0,o.kt)("hr",null),(0,o.kt)("p",null,"Using a single API, a Fragment can be quickly added to the scene.\nEverything else is taken care of by ",(0,o.kt)("inlineCode",{parentName:"p"},"fragment.load"),", which makes it easier to render a ",(0,o.kt)("strong",{parentName:"p"},"Fragment")," file.\ud83d\udcaa\ud83d\udcaa"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},'let uuid: string = "";\n\nasync function loadFragments() {\n if (fragments.groups.size) {\n return;\n }\n const file = await fetch("https://thatopen.github.io/engine_components/resources/small.frag");\n const data = await file.arrayBuffer();\n const buffer = new Uint8Array(data);\n const group = fragments.load(buffer);\n world.scene.three.add(group);\n uuid = group.uuid;\n console.log(group);\n}\n')),(0,o.kt)("admonition",{title:"Loading IFC files as Fragment",type:"tip"},(0,o.kt)("p",{parentName:"admonition"},"You're probably wondering how IFC files can be loaded as Fragment.\nThe same approach can be used to load an IFC file with a fragment;\n",(0,o.kt)("a",{parentName:"p",href:"./FragmentIfcLoader.mdx"},"view its own tutorial")," for further information.")),(0,o.kt)("h3",{id:"-storing-fragment"},"\ud83d\udce4 Storing Fragment"),(0,o.kt)("hr",null),(0,o.kt)("p",null,(0,o.kt)("strong",{parentName:"p"},"Fragment Manager")," provides us with option to export the Fragment we have used in our Scene.\nFragment are exported to ",(0,o.kt)("strong",{parentName:"p"},(0,o.kt)("a",{parentName:"strong",href:"https://developer.mozilla.org/en-US/docs/Web/API/Blob/Blob"},"Blob")),",\nwhich can be used to generate a File and then download it.\u2197\ufe0f\nLet's see how you can use ",(0,o.kt)("inlineCode",{parentName:"p"},"fragment.export")," in your code."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},'function download(file: File) {\n const link = document.createElement("a");\n link.href = URL.createObjectURL(file);\n link.download = file.name;\n document.body.appendChild(link);\n link.click();\n link.remove();\n}\n\nfunction exportFragments() {\n if (!fragments.groups.size) {\n return;\n }\n const group = fragments.groups.get(uuid);\n if (!group) {\n return;\n }\n const data = fragments.export(group);\n const blob = new Blob([data]);\n const file = new File([blob], "small.frag");\n download(file);\n}\n')),(0,o.kt)("admonition",{title:"Creating Dynamic URLs",type:"info"},(0,o.kt)("p",{parentName:"admonition"},(0,o.kt)("inlineCode",{parentName:"p"},"URL.createObjectURL()")," comes handy when you want to generate a URL for files that were generated programmatically.\nYou can read more about it ",(0,o.kt)("a",{parentName:"p",href:"https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL_static"},"here"),".")),(0,o.kt)("h3",{id:"-discard-fragment-and-clean-the-scene"},"\ud83e\uddf9 Discard Fragment and Clean the Scene"),(0,o.kt)("hr",null),(0,o.kt)("p",null,"At some point, you will require to render numerous Fragment and discard them when not needed.\nYou can use ",(0,o.kt)("inlineCode",{parentName:"p"},"dispose()")," method which will remove the Fragment Meshes from the Scene.\u2702\ufe0f\nAfter using ",(0,o.kt)("inlineCode",{parentName:"p"},"fragments.dispose()"),", you should reinitialize ",(0,o.kt)("strong",{parentName:"p"},"Fragment Manager")," to maintain it's original state for further uses."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},"function disposeFragments() {\n fragments.dispose();\n}\n")),(0,o.kt)("p",null,"Loading a .zip fragment that you have locally is also quite easy:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},'function importExternalFragment() {\n if (fragments.groups.size) {\n return;\n }\n const input = document.createElement("input");\n input.type = "file";\n input.onchange = async () => {\n if (!(input.files && input.files[0])) return;\n const file = input.files[0];\n if (file.name.includes(".frag")) {\n const url = URL.createObjectURL(file);\n const result = await fetch(url);\n const data = await result.arrayBuffer();\n const buffer = new Uint8Array(data);\n fragments.load(buffer);\n }\n input.remove();\n };\n input.click();\n}\n\nconst gui = new dat.GUI();\n\nconst actions = {\n loadFragments: () => loadFragments(),\n disposeFragments: () => disposeFragments(),\n exportFragments: () => exportFragments(),\n importExternalFragment: () => importExternalFragment(),\n};\n\ngui.add(actions, "loadFragments").name("Load Fragments");\ngui.add(actions, "disposeFragments").name("Dispose Fragments");\ngui.add(actions, "exportFragments").name("Export Fragments");\ngui.add(actions, "importExternalFragment").name("Import External Fragment");\n')),(0,o.kt)("p",null,(0,o.kt)("strong",{parentName:"p"},"Congratulations")," \ud83c\udf89 on completing this short yet powerful tutorial!\nNow you can render or export Fragment files in your BIM Apps using ",(0,o.kt)("strong",{parentName:"p"},(0,o.kt)("a",{parentName:"strong",href:"../api/classes/components.FragmentManager"},"Fragment Manager"))," \ud83c\udfaf\nLet's keep it up and check out another tutorial! \ud83c\udf93"),(0,o.kt)("iframe",{src:"https://thatopen.github.io/engine_components/examples/FragmentsManager"}))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/build/assets/js/5c7b714e.ff7650b5.js b/build/assets/js/5c7b714e.ff7650b5.js new file mode 100644 index 000000000..93a2ddb85 --- /dev/null +++ b/build/assets/js/5c7b714e.ff7650b5.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkdocs=self.webpackChunkdocs||[]).push([[4283],{3905:(e,n,t)=>{t.d(n,{Zo:()=>m,kt:()=>g});var a=t(7294);function o(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function r(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);n&&(a=a.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,a)}return t}function s(e){for(var n=1;n=0||(o[t]=e[t]);return o}(e,n);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(o[t]=e[t])}return o}var i=a.createContext({}),p=function(e){var n=a.useContext(i),t=n;return e&&(t="function"==typeof e?e(n):s(s({},n),e)),t},m=function(e){var n=p(e.components);return a.createElement(i.Provider,{value:n},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var n=e.children;return a.createElement(a.Fragment,{},n)}},c=a.forwardRef((function(e,n){var t=e.components,o=e.mdxType,r=e.originalType,i=e.parentName,m=l(e,["components","mdxType","originalType","parentName"]),u=p(t),c=o,g=u["".concat(i,".").concat(c)]||u[c]||d[c]||r;return t?a.createElement(g,s(s({ref:n},m),{},{components:t})):a.createElement(g,s({ref:n},m))}));function g(e,n){var t=arguments,o=n&&n.mdxType;if("string"==typeof e||o){var r=t.length,s=new Array(r);s[0]=c;var l={};for(var i in n)hasOwnProperty.call(n,i)&&(l[i]=n[i]);l.originalType=e,l[u]="string"==typeof e?e:o,s[1]=l;for(var p=2;p{t.r(n),t.d(n,{assets:()=>m,contentTitle:()=>i,default:()=>g,frontMatter:()=>l,metadata:()=>p,toc:()=>u});var a=t(7462),o=t(3366),r=(t(7294),t(3905)),s=["components"],l={},i=void 0,p={unversionedId:"Tutorials/Components/Core/FragmentsManager",id:"Tutorials/Components/Core/FragmentsManager",title:"FragmentsManager",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/FragmentsManager.mdx",sourceDirName:"Tutorials/Components/Core",slug:"/Tutorials/Components/Core/FragmentsManager",permalink:"/Tutorials/Components/Core/FragmentsManager",draft:!1,tags:[],version:"current",frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"Exploder",permalink:"/Tutorials/Components/Core/Exploder"},next:{title:"Grids",permalink:"/Tutorials/Components/Core/Grids"}},m={},u=[{value:"\ud83d\ude80 Handling BIM models like a boss",id:"-handling-bim-models-like-a-boss",level:3},{value:"\ud83c\udf0e Setting up a simple scene",id:"-setting-up-a-simple-scene",level:3},{value:"\ud83e\uddf6 Loading a fragments model",id:"-loading-a-fragments-model",level:3},{value:"\ud83d\udce4 Storing Fragments",id:"-storing-fragments",level:3},{value:"\ud83e\uddf9 Discard Fragment and Clean the Scene",id:"-discard-fragment-and-clean-the-scene",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}],d={toc:u},c="wrapper";function g(e){var n=e.components,t=(0,o.Z)(e,s);return(0,r.kt)(c,(0,a.Z)({},d,t,{components:n,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/fragments/FragmentsManager/example.ts"},"here"),".")),(0,r.kt)("h3",{id:"-handling-bim-models-like-a-boss"},"\ud83d\ude80 Handling BIM models like a boss"),(0,r.kt)("hr",null),(0,r.kt)("p",null,"In this tutorial, you'll learn how to load your BIM models in Fragment format. Fragment is an ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/ThatOpen/engine_fragment/"},"open source geometry system")," that we created on top of ",(0,r.kt)("a",{parentName:"p",href:"https://threejs.org/"},"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."),(0,r.kt)("admonition",{title:"How do I get a BIM model in Fragment format?",type:"tip"},(0,r.kt)("p",{parentName:"admonition"},"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.")),(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"},"Three.js")," to get some 3D entities for our app."),(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 * as THREE from "three";\nimport Stats from "stats.js";\nimport * as OBC from "@thatopen/components";\nimport * as BUI from "@thatopen/ui";\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-fragments-model"},"\ud83e\uddf6 Loading a fragments model"),(0,r.kt)("hr",null),(0,r.kt)("p",null,"Let's begin by getting the FragmentsManager, which is the component to load, export, get and dispose Fragments in your app.\ud83c\udfed"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},"const fragments = components.get(OBC.FragmentsManager);\n")),(0,r.kt)("p",null,"Now we can load a fragment from a file. We will fetch the model data and use the ",(0,r.kt)("inlineCode",{parentName:"p"},"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."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},'let uuid = "";\n\nasync function loadFragments() {\n if (fragments.groups.size) {\n return;\n }\n const file = await fetch(\n "https://thatopen.github.io/engine_components/resources/small.frag",\n );\n const data = await file.arrayBuffer();\n const buffer = new Uint8Array(data);\n const group = fragments.load(buffer);\n world.scene.three.add(group);\n uuid = group.uuid;\n}\n')),(0,r.kt)("h3",{id:"-storing-fragments"},"\ud83d\udce4 Storing Fragments"),(0,r.kt)("hr",null),(0,r.kt)("p",null,"Let's see how you can export fragments as a file. First, we'll define a function to download a file:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},'function download(file: File) {\n const link = document.createElement("a");\n link.href = URL.createObjectURL(file);\n link.download = file.name;\n document.body.appendChild(link);\n link.click();\n link.remove();\n}\n')),(0,r.kt)("p",null,(0,r.kt)("strong",{parentName:"p"},"Fragments Manager")," can export fragments using the ",(0,r.kt)("inlineCode",{parentName:"p"},"export")," method. The method takes the UUID of a fragment as an argument and returns a ",(0,r.kt)("strong",{parentName:"p"},(0,r.kt)("a",{parentName:"strong",href:"https://developer.mozilla.org/en-US/docs/Web/API/Blob/Blob"},"Blob")),", which can be used to generate a File and then download it using the function defined just before.\u2197\ufe0f"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},'function exportFragments() {\n if (!fragments.groups.size) {\n return;\n }\n const group = fragments.groups.get(uuid);\n if (!group) {\n return;\n }\n const data = fragments.export(group);\n const blob = new Blob([data]);\n const file = new File([blob], "small.frag");\n download(file);\n}\n')),(0,r.kt)("h3",{id:"-discard-fragment-and-clean-the-scene"},"\ud83e\uddf9 Discard Fragment and Clean the Scene"),(0,r.kt)("hr",null),(0,r.kt)("p",null,'When your user "closes" one or many BIM models, you\'ll need to discard that FragmetsGroup. You can dispose a specific FragmentsGroup using the ',(0,r.kt)("inlineCode",{parentName:"p"},"disposeGroup")," method, or dispose all FragmentsGroups using the ",(0,r.kt)("inlineCode",{parentName:"p"},"dispose")," method."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},"function disposeFragments() {\n fragments.dispose();\n}\n")),(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 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!"),(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 \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 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!"),(0,r.kt)("iframe",{src:"https://thatopen.github.io/engine_components/examples/FragmentsManager"}))}g.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/build/assets/js/60f3e948.2429a965.js b/build/assets/js/60f3e948.2429a965.js deleted file mode 100644 index 4172d689c..000000000 --- a/build/assets/js/60f3e948.2429a965.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkdocs=self.webpackChunkdocs||[]).push([[1513],{3905:(e,n,t)=>{t.d(n,{Zo:()=>m,kt:()=>h});var a=t(7294);function o(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function r(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);n&&(a=a.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,a)}return t}function i(e){for(var n=1;n=0||(o[t]=e[t]);return o}(e,n);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(o[t]=e[t])}return o}var s=a.createContext({}),p=function(e){var n=a.useContext(s),t=n;return e&&(t="function"==typeof e?e(n):i(i({},n),e)),t},m=function(e){var n=p(e.components);return a.createElement(s.Provider,{value:n},e.children)},u="mdxType",c={inlineCode:"code",wrapper:function(e){var n=e.children;return a.createElement(a.Fragment,{},n)}},d=a.forwardRef((function(e,n){var t=e.components,o=e.mdxType,r=e.originalType,s=e.parentName,m=l(e,["components","mdxType","originalType","parentName"]),u=p(t),d=o,h=u["".concat(s,".").concat(d)]||u[d]||c[d]||r;return t?a.createElement(h,i(i({ref:n},m),{},{components:t})):a.createElement(h,i({ref:n},m))}));function h(e,n){var t=arguments,o=n&&n.mdxType;if("string"==typeof e||o){var r=t.length,i=new Array(r);i[0]=d;var l={};for(var s in n)hasOwnProperty.call(n,s)&&(l[s]=n[s]);l.originalType=e,l[u]="string"==typeof e?e:o,i[1]=l;for(var p=2;p{t.r(n),t.d(n,{assets:()=>m,contentTitle:()=>s,default:()=>h,frontMatter:()=>l,metadata:()=>p,toc:()=>u});var a=t(7462),o=t(3366),r=(t(7294),t(3905)),i=["components"],l={},s=void 0,p={unversionedId:"Tutorials/Components/Core/MiniMap",id:"Tutorials/Components/Core/MiniMap",title:"MiniMap",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/MiniMap.mdx",sourceDirName:"Tutorials/Components/Core",slug:"/Tutorials/Components/Core/MiniMap",permalink:"/Tutorials/Components/Core/MiniMap",draft:!1,tags:[],version:"current",frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"IfcRelationsIndexer",permalink:"/Tutorials/Components/Core/IfcRelationsIndexer"},next:{title:"OrthoPerspectiveCamera",permalink:"/Tutorials/Components/Core/OrthoPerspectiveCamera"}},m={},u=[{value:"\ud83d\uddfa\ufe0f Orientating your user in the scene",id:"\ufe0f-orientating-your-user-in-the-scene",level:3},{value:"\ud83c\udf0e Setting up a simple scene",id:"-setting-up-a-simple-scene",level:3},{value:"\ud83c\udfe0 Loading a model",id:"-loading-a-model",level:3},{value:"\ud83d\uddfa Setting up the map",id:"-setting-up-the-map",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}],c={toc:u},d="wrapper";function h(e){var n=e.components,t=(0,o.Z)(e,i);return(0,r.kt)(d,(0,a.Z)({},c,t,{components:n,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/core/MiniMap/example.ts"},"here"),".")),(0,r.kt)("h3",{id:"\ufe0f-orientating-your-user-in-the-scene"},"\ud83d\uddfa\ufe0f Orientating your user in the scene"),(0,r.kt)("hr",null),(0,r.kt)("p",null,"In this tutorial you'll learn how to use the Minimap, which is a small 2D representation of the 3D world."),(0,r.kt)("admonition",{title:"Do you mean a floorplans?",type:"tip"},(0,r.kt)("p",{parentName:"admonition"},"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. ")),(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"},"Three.js")," to get some 3D entities for our app."),(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"},"@thatopen/ui")," to add some simple and cool UI menus."),(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 THREE from "three";\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);\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\nworld.scene.setup();\n\ncomponents.init();\n\nconst grids = components.get(OBC.Grids);\ngrids.create(world);\n\nworld.camera.controls.setLookAt(1, 2, -2, -2, 0, -5);\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-model"},"\ud83c\udfe0 Loading a model"),(0,r.kt)("hr",null),(0,r.kt)("p",null,"Now that we have a scene, let's load a model. We will use the Fragment Manager for it. "),(0,r.kt)("admonition",{title:"Showing Fragments in the Scene",type:"info"},(0,r.kt)("p",{parentName:"admonition"},"\ud83c\udfd4\ufe0f There is a dedicated tutorial on how to use Fragment Manager to load ",(0,r.kt)("strong",{parentName:"p"},"IFC files"),", check it out if you haven't already!")),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},'const fragments = new OBC.FragmentsManager(components);\n\nconst file = await fetch(\n "https://thatopen.github.io/engine_components/resources/small.frag",\n);\nconst dataBlob = await file.arrayBuffer();\nconst buffer = new Uint8Array(dataBlob);\nconst model = fragments.load(buffer);\nworld.scene.three.add(model);\n')),(0,r.kt)("h3",{id:"-setting-up-the-map"},"\ud83d\uddfa Setting up the map"),(0,r.kt)("hr",null),(0,r.kt)("p",null,"Now, that we have our setup ready. Let's start with the exciting stuff.\nWe will use MiniMap component, which does all the work for us.\ud83d\udd2e"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},"const maps = new OBC.MiniMaps(components);\nconst map = maps.create(world);\n")),(0,r.kt)("p",null,"We have already created a simple DIV element with id ",(0,r.kt)("inlineCode",{parentName:"p"},"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."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},'const mapContainer = document.getElementById("minimap") as HTMLDivElement;\nconst canvas = map.renderer.domElement;\ncanvas.style.borderRadius = "12px";\nmapContainer.append(canvas);\nmap.resize();\n')),(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,"we'll also need a reference to the size of the minimap to control it:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},"const mapSize = map.getSize();\n")),(0,r.kt)("p",null,"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!"),(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 \n \n \n \n \n \n \n \n
\n \n \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! 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."),(0,r.kt)("iframe",{src:"https://thatopen.github.io/engine_components/examples/MiniMap"}))}h.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/build/assets/js/60f3e948.ad52528c.js b/build/assets/js/60f3e948.ad52528c.js new file mode 100644 index 000000000..7bd50ac19 --- /dev/null +++ b/build/assets/js/60f3e948.ad52528c.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkdocs=self.webpackChunkdocs||[]).push([[1513],{3905:(e,n,t)=>{t.d(n,{Zo:()=>m,kt:()=>h});var a=t(7294);function o(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function r(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);n&&(a=a.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,a)}return t}function i(e){for(var n=1;n=0||(o[t]=e[t]);return o}(e,n);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(o[t]=e[t])}return o}var s=a.createContext({}),p=function(e){var n=a.useContext(s),t=n;return e&&(t="function"==typeof e?e(n):i(i({},n),e)),t},m=function(e){var n=p(e.components);return a.createElement(s.Provider,{value:n},e.children)},u="mdxType",c={inlineCode:"code",wrapper:function(e){var n=e.children;return a.createElement(a.Fragment,{},n)}},d=a.forwardRef((function(e,n){var t=e.components,o=e.mdxType,r=e.originalType,s=e.parentName,m=l(e,["components","mdxType","originalType","parentName"]),u=p(t),d=o,h=u["".concat(s,".").concat(d)]||u[d]||c[d]||r;return t?a.createElement(h,i(i({ref:n},m),{},{components:t})):a.createElement(h,i({ref:n},m))}));function h(e,n){var t=arguments,o=n&&n.mdxType;if("string"==typeof e||o){var r=t.length,i=new Array(r);i[0]=d;var l={};for(var s in n)hasOwnProperty.call(n,s)&&(l[s]=n[s]);l.originalType=e,l[u]="string"==typeof e?e:o,i[1]=l;for(var p=2;p{t.r(n),t.d(n,{assets:()=>m,contentTitle:()=>s,default:()=>h,frontMatter:()=>l,metadata:()=>p,toc:()=>u});var a=t(7462),o=t(3366),r=(t(7294),t(3905)),i=["components"],l={},s=void 0,p={unversionedId:"Tutorials/Components/Core/MiniMap",id:"Tutorials/Components/Core/MiniMap",title:"MiniMap",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/MiniMap.mdx",sourceDirName:"Tutorials/Components/Core",slug:"/Tutorials/Components/Core/MiniMap",permalink:"/Tutorials/Components/Core/MiniMap",draft:!1,tags:[],version:"current",frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"IfcRelationsIndexer",permalink:"/Tutorials/Components/Core/IfcRelationsIndexer"},next:{title:"OrthoPerspectiveCamera",permalink:"/Tutorials/Components/Core/OrthoPerspectiveCamera"}},m={},u=[{value:"\ud83d\uddfa\ufe0f Orientating your user in the scene",id:"\ufe0f-orientating-your-user-in-the-scene",level:3},{value:"\ud83c\udf0e Setting up a simple scene",id:"-setting-up-a-simple-scene",level:3},{value:"\ud83c\udfe0 Loading a model",id:"-loading-a-model",level:3},{value:"\ud83d\uddfa Setting up the map",id:"-setting-up-the-map",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}],c={toc:u},d="wrapper";function h(e){var n=e.components,t=(0,o.Z)(e,i);return(0,r.kt)(d,(0,a.Z)({},c,t,{components:n,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/core/MiniMap/example.ts"},"here"),".")),(0,r.kt)("h3",{id:"\ufe0f-orientating-your-user-in-the-scene"},"\ud83d\uddfa\ufe0f Orientating your user in the scene"),(0,r.kt)("hr",null),(0,r.kt)("p",null,"In this tutorial you'll learn how to use the Minimap, which is a small 2D representation of the 3D world."),(0,r.kt)("admonition",{title:"Do you mean a floorplan?",type:"tip"},(0,r.kt)("p",{parentName:"admonition"},"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. ")),(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"},"Three.js")," to get some 3D entities for our app."),(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"},"@thatopen/ui")," to add some simple and cool UI menus."),(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 THREE from "three";\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);\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\nworld.scene.setup();\n\ncomponents.init();\n\nconst grids = components.get(OBC.Grids);\ngrids.create(world);\n\nworld.camera.controls.setLookAt(1, 2, -2, -2, 0, -5);\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-model"},"\ud83c\udfe0 Loading a model"),(0,r.kt)("hr",null),(0,r.kt)("p",null,"Now that we have a scene, let's load a model. We will use the Fragment Manager for it. "),(0,r.kt)("admonition",{title:"Showing Fragments in the Scene",type:"info"},(0,r.kt)("p",{parentName:"admonition"},"\ud83c\udfd4\ufe0f There is a dedicated tutorial on how to use Fragment Manager to load ",(0,r.kt)("strong",{parentName:"p"},"IFC files"),", check it out if you haven't already!")),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},'const fragments = new OBC.FragmentsManager(components);\n\nconst file = await fetch(\n "https://thatopen.github.io/engine_components/resources/small.frag",\n);\nconst dataBlob = await file.arrayBuffer();\nconst buffer = new Uint8Array(dataBlob);\nconst model = fragments.load(buffer);\nworld.scene.three.add(model);\n')),(0,r.kt)("h3",{id:"-setting-up-the-map"},"\ud83d\uddfa Setting up the map"),(0,r.kt)("hr",null),(0,r.kt)("p",null,"Now, that we have our setup ready. Let's start with the exciting stuff.\nWe will use MiniMap component, which does all the work for us.\ud83d\udd2e"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},"const maps = new OBC.MiniMaps(components);\nconst map = maps.create(world);\n")),(0,r.kt)("p",null,"We have already created a simple DIV element with id ",(0,r.kt)("inlineCode",{parentName:"p"},"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."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},'const mapContainer = document.getElementById("minimap") as HTMLDivElement;\nconst canvas = map.renderer.domElement;\ncanvas.style.borderRadius = "12px";\nmapContainer.append(canvas);\nmap.resize();\n')),(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,"we'll also need a reference to the size of the minimap to control it:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},"const mapSize = map.getSize();\n")),(0,r.kt)("p",null,"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!"),(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 \n \n \n \n
\n \n \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! 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."),(0,r.kt)("iframe",{src:"https://thatopen.github.io/engine_components/examples/MiniMap"}))}h.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/build/assets/js/7899e7db.1dee1e61.js b/build/assets/js/7899e7db.1dee1e61.js new file mode 100644 index 000000000..fb2bcc40e --- /dev/null +++ b/build/assets/js/7899e7db.1dee1e61.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkdocs=self.webpackChunkdocs||[]).push([[3501],{3905:(e,t,n)=>{n.d(t,{Zo:()=>m,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 l(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):l(l({},t),e)),n},m=function(e){var t=p(e.components);return o.createElement(s.Provider,{value:t},e.children)},c="mdxType",d={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,m=i(e,["components","mdxType","originalType","parentName"]),c=p(n),u=a,h=c["".concat(s,".").concat(u)]||c[u]||d[u]||r;return n?o.createElement(h,l(l({ref:t},m),{},{components:n})):o.createElement(h,l({ref:t},m))}));function h(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var r=n.length,l=new Array(r);l[0]=u;var i={};for(var s in t)hasOwnProperty.call(t,s)&&(i[s]=t[s]);i.originalType=e,i[c]="string"==typeof e?e:a,l[1]=i;for(var p=2;p{n.r(t),n.d(t,{assets:()=>m,contentTitle:()=>s,default:()=>h,frontMatter:()=>i,metadata:()=>p,toc:()=>c});var o=n(7462),a=n(3366),r=(n(7294),n(3905)),l=["components"],i={},s=void 0,p={unversionedId:"Tutorials/Components/Core/Exploder",id:"Tutorials/Components/Core/Exploder",title:"Exploder",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/Exploder.mdx",sourceDirName:"Tutorials/Components/Core",slug:"/Tutorials/Components/Core/Exploder",permalink:"/Tutorials/Components/Core/Exploder",draft:!1,tags:[],version:"current",frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"Cullers",permalink:"/Tutorials/Components/Core/Cullers"},next:{title:"FragmentsManager",permalink:"/Tutorials/Components/Core/FragmentsManager"}},m={},c=[{value:"\ud83e\udde8 Exploding BIM models",id:"-exploding-bim-models",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:"\ud83e\udd2f Boom!",id:"-boom",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}],d={toc:c},u="wrapper";function h(e){var t=e.components,n=(0,a.Z)(e,l);return(0,r.kt)(u,(0,o.Z)({},d,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/fragments/Exploder/example.ts"},"here"),".")),(0,r.kt)("h3",{id:"-exploding-bim-models"},"\ud83e\udde8 Exploding BIM models"),(0,r.kt)("hr",null),(0,r.kt)("p",null,"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."),(0,r.kt)("admonition",{title:"Exploding, like bombs?",type:"tip"},(0,r.kt)("p",{parentName:"admonition"},'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.')),(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"},"web-ifc")," to get some IFC items."),(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 WEBIFC from "web-ifc";\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 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."),(0,r.kt)("admonition",{title:"Fragments?",type:"tip"},(0,r.kt)("p",{parentName:"admonition"},"If you are not familiar with fragments, check out the IfcLoader tutorial!")),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},'const fragments = new OBC.FragmentsManager(components);\nconst file = await fetch(\n "https://thatopen.github.io/engine_components/resources/small.frag",\n);\nconst data = await file.arrayBuffer();\nconst buffer = new Uint8Array(data);\nconst model = fragments.load(buffer);\nworld.scene.three.add(model);\n\nconst properties = await fetch("https://thatopen.github.io/engine_components/resources/small.json");\nmodel.setLocalProperties(await properties.json());\n')),(0,r.kt)("h3",{id:"-boom"},"\ud83e\udd2f Boom!"),(0,r.kt)("hr",null),(0,r.kt)("p",null,"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."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},"const exploder = components.get(OBC.Exploder);\n")),(0,r.kt)("p",null,"Before being able to use it, we will need to get the classifier to classify the items of the model by storey."),(0,r.kt)("admonition",{title:"Classify?",type:"tip"},(0,r.kt)("p",{parentName:"admonition"},"If you are not familiar with the classifier, check out its specific tutorial!")),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},'const classifier = components.get(OBC.Classifier);\nclassifier.byIfcRel(model, WEBIFC.IFCRELCONTAINEDINSPATIALSTRUCTURE, "storeys");\n')),(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 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!"),(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\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! 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!"),(0,r.kt)("iframe",{src:"https://thatopen.github.io/engine_components/examples/Exploder"}))}h.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/build/assets/js/7cc95c1b.ea78333f.js b/build/assets/js/7cc95c1b.ea78333f.js new file mode 100644 index 000000000..8044deda2 --- /dev/null +++ b/build/assets/js/7cc95c1b.ea78333f.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkdocs=self.webpackChunkdocs||[]).push([[9595],{3905:(e,t,n)=>{n.d(t,{Zo:()=>c,kt:()=>h});var r=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 o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function i(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var p=r.createContext({}),s=function(e){var t=r.useContext(p),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},c=function(e){var t=s(e.components);return r.createElement(p.Provider,{value:t},e.children)},d="mdxType",u={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},m=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,p=e.parentName,c=l(e,["components","mdxType","originalType","parentName"]),d=s(n),m=a,h=d["".concat(p,".").concat(m)]||d[m]||u[m]||o;return n?r.createElement(h,i(i({ref:t},c),{},{components:n})):r.createElement(h,i({ref:t},c))}));function h(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,i=new Array(o);i[0]=m;var l={};for(var p in t)hasOwnProperty.call(t,p)&&(l[p]=t[p]);l.originalType=e,l[d]="string"==typeof e?e:a,i[1]=l;for(var s=2;s{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>p,default:()=>h,frontMatter:()=>l,metadata:()=>s,toc:()=>d});var r=n(7462),a=n(3366),o=(n(7294),n(3905)),i=["components"],l={},p=void 0,s={unversionedId:"Tutorials/UserInterface/OBC/ElementProperties",id:"Tutorials/UserInterface/OBC/ElementProperties",title:"ElementProperties",description:"Copying and pasting? We've got you covered! You can find the full source code of this tutorial here.",source:"@site/docs/Tutorials/UserInterface/OBC/ElementProperties.mdx",sourceDirName:"Tutorials/UserInterface/OBC",slug:"/Tutorials/UserInterface/OBC/ElementProperties",permalink:"/Tutorials/UserInterface/OBC/ElementProperties",draft:!1,tags:[],version:"current",frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"ClassificationsTree",permalink:"/Tutorials/UserInterface/OBC/ClassificationsTree"},next:{title:"EntityAttributes",permalink:"/Tutorials/UserInterface/OBC/EntityAttributes"}},c={},d=[{value:"Displaying data the simplest way \ud83d\udd25\ud83d\udd25",id:"displaying-data-the-simplest-way-",level:2},{value:"Loading a model and computing it's relations",id:"loading-a-model-and-computing-its-relations",level:3},{value:"Creating the properties table",id:"creating-the-properties-table",level:3},{value:"Creating a panel to append the table",id:"creating-a-panel-to-append-the-table",level:3}],u={toc:d},m="wrapper";function h(e){var t=e.components,n=(0,a.Z)(e,i);return(0,o.kt)(m,(0,r.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("admonition",{title:"Source",type:"info"},(0,o.kt)("p",{parentName:"admonition"},"Copying and pasting? We've got you covered! You can find the full source code of this tutorial ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/ThatOpen/engine_ui-components/blob/main/packages/obc/src/components/tables/ElementProperties/example.ts"},"here"),".")),(0,o.kt)("h2",{id:"displaying-data-the-simplest-way-"},"Displaying data the simplest way \ud83d\udd25\ud83d\udd25"),(0,o.kt)("hr",null),(0,o.kt)("p",null,"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!"),(0,o.kt)("h3",{id:"loading-a-model-and-computing-its-relations"},"Loading a model and computing it's relations"),(0,o.kt)("p",null,"First things first... let's load a model \ud83d\udc47"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},'const ifcLoader = components.get(OBC.IfcLoader);\nawait ifcLoader.setup();\nconst file = await fetch(\n "https://thatopen.github.io/engine_ui-components/resources/small.ifc",\n);\nconst buffer = await file.arrayBuffer();\nconst typedArray = new Uint8Array(buffer);\nconst model = await ifcLoader.load(typedArray);\nworld.scene.three.add(model);\n')),(0,o.kt)("admonition",{type:"tip"},(0,o.kt)("p",{parentName:"admonition"},"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?")),(0,o.kt)("p",null,"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 ",(0,o.kt)("a",{parentName:"p",href:"/Tutorials/Components/Core/IfcRelationsIndexer"},"IfcRelationsIndexer")," component from ",(0,o.kt)("inlineCode",{parentName:"p"},"@thatopen/components")," to speed up the process."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},"const indexer = components.get(OBC.IfcRelationsIndexer);\nawait indexer.process(model);\n")),(0,o.kt)("p",null,"Once the relations are processed, the ",(0,o.kt)("inlineCode",{parentName:"p"},"Element Properties")," component has everything it needs in order to display the properties in a cool way \ud83d\ude0e."),(0,o.kt)("h3",{id:"creating-the-properties-table"},"Creating the properties table"),(0,o.kt)("p",null,"Let's create an instance of the functional component, like this:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},"const [propertiesTable, updatePropertiesTable] = CUI.tables.elementProperties({\n components,\n fragmentIdMap: {},\n});\n\npropertiesTable.preserveStructureOnFilter = true;\npropertiesTable.indentationInText = false;\n")),(0,o.kt)("admonition",{type:"tip"},(0,o.kt)("p",{parentName:"admonition"},"The ",(0,o.kt)("inlineCode",{parentName:"p"},"elementProperties")," functional component is a simplified version that shows any model entity data. However, if you like a more complete properties table, use the ",(0,o.kt)("inlineCode",{parentName:"p"},"entityAttributes")," component.")),(0,o.kt)("p",null,"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 ",(0,o.kt)("inlineCode",{parentName:"p"},"@thatopen/components-front"),":"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},"const highlighter = components.get(OBF.Highlighter);\nhighlighter.setup({ world });\n\nhighlighter.events.select.onHighlight.add((fragmentIdMap) => {\n updatePropertiesTable({ fragmentIdMap });\n});\n\nhighlighter.events.select.onClear.add(() =>\n updatePropertiesTable({ fragmentIdMap: {} }),\n);\n")),(0,o.kt)("h3",{id:"creating-a-panel-to-append-the-table"},"Creating a panel to append the table"),(0,o.kt)("p",null,"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 \ud83d\ude09"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},'const propertiesPanel = BUI.Component.create(() => {\n const onTextInput = (e: Event) => {\n const input = e.target as BUI.TextInput;\n propertiesTable.queryString = input.value !== "" ? input.value : null;\n };\n\n const expandTable = (e: Event) => {\n const button = e.target as BUI.Button;\n propertiesTable.expanded = !propertiesTable.expanded;\n button.label = propertiesTable.expanded ? "Collapse" : "Expand";\n };\n\n const copyAsTSV = async () => {\n await navigator.clipboard.writeText(propertiesTable.tsv);\n };\n\n return BUI.html`\n \n \n
\n \n \n
\n \n ${propertiesTable}\n
\n
\n `;\n});\n')),(0,o.kt)("p",null,"Finally, let's create a BIM Grid element and provide both the panel and the viewport to display everything."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},'const app = document.createElement("bim-grid");\napp.layouts = {\n main: {\n template: `\n "propertiesPanel viewport"\n /25rem 1fr\n `,\n elements: { propertiesPanel, viewport },\n },\n};\n\napp.layout = "main";\ndocument.body.append(app);\n')),(0,o.kt)("p",null,"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! \ud83d\udcaa"),(0,o.kt)("iframe",{src:"https://thatopen.github.io/engine_ui-components/examples/ElementProperties"}))}h.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/build/assets/js/7cc95c1b.fcef41d0.js b/build/assets/js/7cc95c1b.fcef41d0.js deleted file mode 100644 index 4667b6c0b..000000000 --- a/build/assets/js/7cc95c1b.fcef41d0.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkdocs=self.webpackChunkdocs||[]).push([[9595],{3905:(e,t,n)=>{n.d(t,{Zo:()=>c,kt:()=>h});var r=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 o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function i(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var p=r.createContext({}),s=function(e){var t=r.useContext(p),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},c=function(e){var t=s(e.components);return r.createElement(p.Provider,{value:t},e.children)},d="mdxType",u={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},m=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,p=e.parentName,c=l(e,["components","mdxType","originalType","parentName"]),d=s(n),m=a,h=d["".concat(p,".").concat(m)]||d[m]||u[m]||o;return n?r.createElement(h,i(i({ref:t},c),{},{components:n})):r.createElement(h,i({ref:t},c))}));function h(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,i=new Array(o);i[0]=m;var l={};for(var p in t)hasOwnProperty.call(t,p)&&(l[p]=t[p]);l.originalType=e,l[d]="string"==typeof e?e:a,i[1]=l;for(var s=2;s{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>p,default:()=>h,frontMatter:()=>l,metadata:()=>s,toc:()=>d});var r=n(7462),a=n(3366),o=(n(7294),n(3905)),i=["components"],l={},p=void 0,s={unversionedId:"Tutorials/UserInterface/OBC/ElementProperties",id:"Tutorials/UserInterface/OBC/ElementProperties",title:"ElementProperties",description:"Copying and pasting? We've got you covered! You can find the full source code of this tutorial here.",source:"@site/docs/Tutorials/UserInterface/OBC/ElementProperties.mdx",sourceDirName:"Tutorials/UserInterface/OBC",slug:"/Tutorials/UserInterface/OBC/ElementProperties",permalink:"/Tutorials/UserInterface/OBC/ElementProperties",draft:!1,tags:[],version:"current",frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"ClassificationsTree",permalink:"/Tutorials/UserInterface/OBC/ClassificationsTree"},next:{title:"EntityAttributes",permalink:"/Tutorials/UserInterface/OBC/EntityAttributes"}},c={},d=[{value:"Displaying data the simplest way \ud83d\udd25\ud83d\udd25",id:"displaying-data-the-simplest-way-",level:2},{value:"Loading a model and computing it's relations",id:"loading-a-model-and-computing-its-relations",level:3},{value:"Creating the properties table",id:"creating-the-properties-table",level:3},{value:"Creating a panel to append the table",id:"creating-a-panel-to-append-the-table",level:3}],u={toc:d},m="wrapper";function h(e){var t=e.components,n=(0,a.Z)(e,i);return(0,o.kt)(m,(0,r.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("admonition",{title:"Source",type:"info"},(0,o.kt)("p",{parentName:"admonition"},"Copying and pasting? We've got you covered! You can find the full source code of this tutorial ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/ThatOpen/engine_ui-components/blob/main/packages/obc/src/components/tables/ElementProperties/example.ts"},"here"),".")),(0,o.kt)("h2",{id:"displaying-data-the-simplest-way-"},"Displaying data the simplest way \ud83d\udd25\ud83d\udd25"),(0,o.kt)("hr",null),(0,o.kt)("p",null,"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!"),(0,o.kt)("h3",{id:"loading-a-model-and-computing-its-relations"},"Loading a model and computing it's relations"),(0,o.kt)("p",null,"First things first... let's load a model \ud83d\udc47"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},'const ifcLoader = components.get(OBC.IfcLoader);\nawait ifcLoader.setup();\nconst file = await fetch("/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,o.kt)("admonition",{type:"tip"},(0,o.kt)("p",{parentName:"admonition"},"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?")),(0,o.kt)("p",null,"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 ",(0,o.kt)("a",{parentName:"p",href:"/Tutorials/Components/Core/IfcRelationsIndexer"},"IfcRelationsIndexer")," component from ",(0,o.kt)("inlineCode",{parentName:"p"},"@thatopen/components")," to speed up the process."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},"const indexer = components.get(OBC.IfcRelationsIndexer);\nawait indexer.process(model);\n")),(0,o.kt)("p",null,"Once the relations are processed, the ",(0,o.kt)("inlineCode",{parentName:"p"},"Element Properties")," component has everything it needs in order to display the properties in a cool way \ud83d\ude0e."),(0,o.kt)("h3",{id:"creating-the-properties-table"},"Creating the properties table"),(0,o.kt)("p",null,"Let's create an instance of the functional component, like this:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},"const [propertiesTable, updatePropertiesTable] = CUI.tables.elementProperties({\n components,\n fragmentIdMap: {},\n});\n\npropertiesTable.preserveStructureOnFilter = true;\npropertiesTable.indentationInText = false;\n")),(0,o.kt)("admonition",{type:"tip"},(0,o.kt)("p",{parentName:"admonition"},"The ",(0,o.kt)("inlineCode",{parentName:"p"},"elementProperties")," functional component is a simplified version that shows any model entity data. However, if you like a more complete properties table, use the ",(0,o.kt)("inlineCode",{parentName:"p"},"entityAttributes")," component.")),(0,o.kt)("p",null,"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 ",(0,o.kt)("inlineCode",{parentName:"p"},"@thatopen/components-front"),":"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},"const highlighter = components.get(OBF.Highlighter);\nhighlighter.setup({ world });\n\nhighlighter.events.select.onHighlight.add((fragmentIdMap) => {\n updatePropertiesTable({ fragmentIdMap });\n});\n\nhighlighter.events.select.onClear.add(() =>\n updatePropertiesTable({ fragmentIdMap: {} }),\n);\n")),(0,o.kt)("h3",{id:"creating-a-panel-to-append-the-table"},"Creating a panel to append the table"),(0,o.kt)("p",null,"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 \ud83d\ude09"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},'const propertiesPanel = BUI.Component.create(() => {\n const onTextInput = (e: Event) => {\n const input = e.target as BUI.TextInput;\n propertiesTable.queryString = input.value !== "" ? input.value : null;\n };\n\n const expandTable = (e: Event) => {\n const button = e.target as BUI.Button;\n propertiesTable.expanded = !propertiesTable.expanded;\n button.label = propertiesTable.expanded ? "Collapse" : "Expand";\n };\n\n const copyAsTSV = async () => {\n await navigator.clipboard.writeText(propertiesTable.tsv);\n };\n\n return BUI.html`\n \n \n
\n \n \n
\n \n ${propertiesTable}\n
\n
\n `;\n});\n')),(0,o.kt)("p",null,"Finally, let's create a BIM Grid element and provide both the panel and the viewport to display everything."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},'const app = document.createElement("bim-grid");\napp.layouts = {\n main: {\n template: `\n "propertiesPanel viewport"\n /25rem 1fr\n `,\n elements: { propertiesPanel, viewport },\n },\n};\n\napp.layout = "main";\ndocument.body.append(app);\n')),(0,o.kt)("p",null,"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! \ud83d\udcaa"),(0,o.kt)("iframe",{src:"https://thatopen.github.io/engine_ui-components/examples/ElementProperties"}))}h.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/build/assets/js/7d516a78.3a2f886f.js b/build/assets/js/7d516a78.b37828df.js similarity index 69% rename from build/assets/js/7d516a78.3a2f886f.js rename to build/assets/js/7d516a78.b37828df.js index b39ac56c1..73dc73301 100644 --- a/build/assets/js/7d516a78.3a2f886f.js +++ b/build/assets/js/7d516a78.b37828df.js @@ -1 +1 @@ -"use strict";(self.webpackChunkdocs=self.webpackChunkdocs||[]).push([[4847],{3905:(e,t,n)=>{n.d(t,{Zo:()=>l,kt:()=>d});var o=n(7294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(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 p(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(o=0;o=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var i=o.createContext({}),c=function(e){var t=o.useContext(i),n=t;return e&&(n="function"==typeof e?e(t):p(p({},t),e)),n},l=function(e){var t=c(e.components);return o.createElement(i.Provider,{value:t},e.children)},m="mdxType",u={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},h=o.forwardRef((function(e,t){var n=e.components,r=e.mdxType,a=e.originalType,i=e.parentName,l=s(e,["components","mdxType","originalType","parentName"]),m=c(n),h=r,d=m["".concat(i,".").concat(h)]||m[h]||u[h]||a;return n?o.createElement(d,p(p({ref:t},l),{},{components:n})):o.createElement(d,p({ref:t},l))}));function d(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var a=n.length,p=new Array(a);p[0]=h;var s={};for(var i in t)hasOwnProperty.call(t,i)&&(s[i]=t[i]);s.originalType=e,s[m]="string"==typeof e?e:r,p[1]=s;for(var c=2;c{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>i,default:()=>d,frontMatter:()=>s,metadata:()=>c,toc:()=>m});var o=n(7462),r=n(3366),a=(n(7294),n(3905)),p=["components"],s={title:"Components"},i=void 0,c={unversionedId:"Tutorials/Components/index",id:"Tutorials/Components/index",title:"Components",description:"TOC",source:"@site/docs/Tutorials/Components/index.md",sourceDirName:"Tutorials/Components",slug:"/Tutorials/Components/",permalink:"/Tutorials/Components/",draft:!1,tags:[],version:"current",frontMatter:{title:"Components"},sidebar:"tutorialSidebar",previous:{title:"QueryGroup",permalink:"/api/interfaces/thatopen_ui.QueryGroup"},next:{title:"BoundingBoxer",permalink:"/Tutorials/Components/Core/BoundingBoxer"}},l={},m=[{value:"Packages",id:"packages",level:3},{value:"Usage",id:"usage",level:3}],u={toc:m},h="wrapper";function d(e){var t=e.components,n=(0,r.Z)(e,p);return(0,a.kt)(h,(0,o.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("p",{align:"center"},(0,a.kt)("a",{href:"https://thatopen.com/"},"TOC"),"|",(0,a.kt)("a",{href:"https://docs.thatopen.com/intro"},"documentation"),"|",(0,a.kt)("a",{href:"https://thatopen.github.io/engine_componentspackages/core/src/fragments/IfcLoader/example.html"},"demo"),"|",(0,a.kt)("a",{href:"https://people.thatopen.com/"},"community"),"|",(0,a.kt)("a",{href:"https://www.npmjs.com/org/thatopen"},"npm package")),(0,a.kt)("p",null,(0,a.kt)("img",{parentName:"p",src:"https://thatopen.github.io/engine_components/resources/cover.png",alt:"cover"})),(0,a.kt)("h1",null,"Open BIM Components ",(0,a.kt)("img",{src:"https://thatopen.github.io/engine_components/resources/favicon.ico",width:"32"})),(0,a.kt)("p",null,(0,a.kt)("a",{parentName:"p",href:"https://www.npmjs.com/package/@thatopen/components"},(0,a.kt)("img",{parentName:"a",src:"https://img.shields.io/npm/v/@thatopen/components",alt:"NPM Package"})),"\n",(0,a.kt)("a",{parentName:"p",href:"https://www.npmjs.com/package/@thatopen/components"},(0,a.kt)("img",{parentName:"a",src:"https://img.shields.io/npm/dw/@thatopen/components",alt:"NPM Package"})),"\n",(0,a.kt)("a",{parentName:"p",href:"https://github.com/ThatOpen/engine_components/actions/workflows/tests.yml"},(0,a.kt)("img",{parentName:"a",src:"https://github.com/ThatOpen/engine_components/actions/workflows/tests.yml/badge.svg",alt:"Tests"}))),(0,a.kt)("p",null,"This library is a collection of BIM tools based on ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/mrdoob/three.js/"},"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. "),(0,a.kt)("h3",{id:"packages"},"Packages"),(0,a.kt)("p",null,"This library contains 2 packages:"),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"@thatopen/components")," - The core functionality. Compatible both with browser and Node.js environments."),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"@thatopen/components-front")," - Features exclusive for browser environments."),(0,a.kt)("h3",{id:"usage"},"Usage"),(0,a.kt)("p",null,"You need to be familiar with ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/mrdoob/three.js/"},"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 ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/ThatOpen/engine_components/blob/main/src/core/SimpleScene/index.html"},"here")," and the deployed app ",(0,a.kt)("a",{parentName:"p",href:"https://thatopen.github.io/engine_components/src/core/SimpleScene/index.html"},"here"),"."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-js"},'/* eslint import/no-extraneous-dependencies: 0 */\n\nimport * as THREE from "three";\nimport * as OBC from "../..";\n\nconst 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\nconst material = new THREE.MeshLambertMaterial({ color: "#6528D7" });\nconst geometry = new THREE.BoxGeometry();\nconst cube = new THREE.Mesh(geometry, material);\nworld.scene.three.add(cube);\n\nworld.scene.setup();\n\nworld.camera.controls.setLookAt(3, 3, 3, 0, 0, 0);\n')))}d.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkdocs=self.webpackChunkdocs||[]).push([[4847],{3905:(e,t,n)=>{n.d(t,{Zo:()=>l,kt:()=>d});var o=n(7294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(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 s(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(o=0;o=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var i=o.createContext({}),c=function(e){var t=o.useContext(i),n=t;return e&&(n="function"==typeof e?e(t):s(s({},t),e)),n},l=function(e){var t=c(e.components);return o.createElement(i.Provider,{value:t},e.children)},m="mdxType",u={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},h=o.forwardRef((function(e,t){var n=e.components,r=e.mdxType,a=e.originalType,i=e.parentName,l=p(e,["components","mdxType","originalType","parentName"]),m=c(n),h=r,d=m["".concat(i,".").concat(h)]||m[h]||u[h]||a;return n?o.createElement(d,s(s({ref:t},l),{},{components:n})):o.createElement(d,s({ref:t},l))}));function d(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var a=n.length,s=new Array(a);s[0]=h;var p={};for(var i in t)hasOwnProperty.call(t,i)&&(p[i]=t[i]);p.originalType=e,p[m]="string"==typeof e?e:r,s[1]=p;for(var c=2;c{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>i,default:()=>d,frontMatter:()=>p,metadata:()=>c,toc:()=>m});var o=n(7462),r=n(3366),a=(n(7294),n(3905)),s=["components"],p={title:"Components"},i=void 0,c={unversionedId:"Tutorials/Components/index",id:"Tutorials/Components/index",title:"Components",description:"TOC",source:"@site/docs/Tutorials/Components/index.md",sourceDirName:"Tutorials/Components",slug:"/Tutorials/Components/",permalink:"/Tutorials/Components/",draft:!1,tags:[],version:"current",frontMatter:{title:"Components"},sidebar:"tutorialSidebar",previous:{title:"QueryGroup",permalink:"/api/interfaces/thatopen_ui.QueryGroup"},next:{title:"BoundingBoxer",permalink:"/Tutorials/Components/Core/BoundingBoxer"}},l={},m=[{value:"Packages",id:"packages",level:3},{value:"Usage",id:"usage",level:3}],u={toc:m},h="wrapper";function d(e){var t=e.components,n=(0,r.Z)(e,s);return(0,a.kt)(h,(0,o.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("p",{align:"center"},(0,a.kt)("a",{href:"https://thatopen.com/"},"TOC"),"|",(0,a.kt)("a",{href:"https://docs.thatopen.com/intro"},"documentation"),"|",(0,a.kt)("a",{href:"https://thatopen.github.io/engine_componentspackages/core/src/fragments/IfcLoader/example.html"},"demo"),"|",(0,a.kt)("a",{href:"https://people.thatopen.com/"},"community"),"|",(0,a.kt)("a",{href:"https://www.npmjs.com/org/thatopen"},"npm package")),(0,a.kt)("p",null,(0,a.kt)("img",{parentName:"p",src:"https://thatopen.github.io/engine_components/resources/cover.png",alt:"cover"})),(0,a.kt)("h1",null,"Open BIM Components ",(0,a.kt)("img",{src:"https://thatopen.github.io/engine_components/resources/favicon.ico",width:"32"})),(0,a.kt)("p",null,(0,a.kt)("a",{parentName:"p",href:"https://www.npmjs.com/package/@thatopen/components"},(0,a.kt)("img",{parentName:"a",src:"https://img.shields.io/npm/v/@thatopen/components",alt:"NPM Package"})),"\n",(0,a.kt)("a",{parentName:"p",href:"https://www.npmjs.com/package/@thatopen/components"},(0,a.kt)("img",{parentName:"a",src:"https://img.shields.io/npm/dw/@thatopen/components",alt:"NPM Package"})),"\n",(0,a.kt)("a",{parentName:"p",href:"https://github.com/ThatOpen/engine_components/actions/workflows/tests.yml"},(0,a.kt)("img",{parentName:"a",src:"https://github.com/ThatOpen/engine_components/actions/workflows/tests.yml/badge.svg",alt:"Tests"}))),(0,a.kt)("p",null,"This library is a collection of BIM tools based on ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/mrdoob/three.js/"},"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. "),(0,a.kt)("h3",{id:"packages"},"Packages"),(0,a.kt)("p",null,"This library contains 2 packages:"),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"@thatopen/components")," - The core functionality. Compatible both with browser and Node.js environments."),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"@thatopen/components-front")," - Features exclusive for browser environments."),(0,a.kt)("h3",{id:"usage"},"Usage"),(0,a.kt)("p",null,"You need to be familiar with ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/mrdoob/three.js/"},"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 ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/ThatOpen/engine_components/blob/main/src/core/Worlds/index.html"},"here")," and the deployed app ",(0,a.kt)("a",{parentName:"p",href:"https://thatopen.github.io/engine_components/packages/core/src/core/Worlds/example.ts"},"here"),"."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-js"},'/* eslint import/no-extraneous-dependencies: 0 */\n\nimport * as THREE from "three";\nimport * as OBC from "../..";\n\nconst 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\nconst material = new THREE.MeshLambertMaterial({ color: "#6528D7" });\nconst geometry = new THREE.BoxGeometry();\nconst cube = new THREE.Mesh(geometry, material);\nworld.scene.three.add(cube);\n\nworld.scene.setup();\n\nworld.camera.controls.setLookAt(3, 3, 3, 0, 0, 0);\n')))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/build/assets/js/935f2afb.f96b1499.js b/build/assets/js/935f2afb.92165716.js similarity index 83% rename from build/assets/js/935f2afb.f96b1499.js rename to build/assets/js/935f2afb.92165716.js index 041b4a3cf..4005a8051 100644 --- a/build/assets/js/935f2afb.f96b1499.js +++ b/build/assets/js/935f2afb.92165716.js @@ -1 +1 @@ -"use strict";(self.webpackChunkdocs=self.webpackChunkdocs||[]).push([[53],{1109:e=>{e.exports=JSON.parse('{"pluginId":"default","version":"current","label":"Next","banner":null,"badge":false,"noIndex":false,"className":"docs-version-current","isLast":true,"docsSidebars":{"tutorialSidebar":[{"type":"link","label":"\ud83d\udc68\ud83c\udffb\u200d\ud83d\udcbb Introduction","href":"/intro","docId":"intro"},{"type":"link","label":"\ud83e\udd1d Get involved","href":"/contributing","docId":"contributing"},{"type":"category","label":"\ud83e\udde9 Components","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"\ud83d\ude80 Getting started","href":"/components/getting-started","docId":"components/getting-started"},{"type":"link","label":"\ud83e\uddbe Making your own","href":"/components/creating-components","docId":"components/creating-components"},{"type":"link","label":"\ud83e\uddf9 Keeping them clean","href":"/components/clean-components-guide","docId":"components/clean-components-guide"},{"type":"link","label":"\ud83e\udded Tutorial paths","href":"/components/tutorial-paths","docId":"components/tutorial-paths"}]},{"type":"category","label":"API","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"Exports","href":"/api/modules","docId":"api/modules"},{"type":"category","label":"Modules","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"@thatopen/components","href":"/api/modules/thatopen_components","docId":"api/modules/thatopen_components"},{"type":"link","label":"@thatopen/components-front","href":"/api/modules/thatopen_components_front","docId":"api/modules/thatopen_components_front"},{"type":"link","label":"@thatopen/fragments","href":"/api/modules/thatopen_fragments","docId":"api/modules/thatopen_fragments"},{"type":"link","label":"@thatopen/ui","href":"/api/modules/thatopen_ui","docId":"api/modules/thatopen_ui"},{"type":"link","label":"@thatopen/ui-obc","href":"/api/modules/thatopen_ui_obc","docId":"api/modules/thatopen_ui_obc"}]},{"type":"category","label":"Classes","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"AsyncEvent","href":"/api/classes/thatopen_components.AsyncEvent","docId":"api/classes/thatopen_components.AsyncEvent"},{"type":"link","label":"Base","href":"/api/classes/thatopen_components.Base","docId":"api/classes/thatopen_components.Base"},{"type":"link","label":"BaseWorldItem","href":"/api/classes/thatopen_components.BaseWorldItem","docId":"api/classes/thatopen_components.BaseWorldItem"},{"type":"link","label":"BoundingBoxer","href":"/api/classes/thatopen_components.BoundingBoxer","docId":"api/classes/thatopen_components.BoundingBoxer"},{"type":"link","label":"Clipper","href":"/api/classes/thatopen_components.Clipper","docId":"api/classes/thatopen_components.Clipper"},{"type":"link","label":"Component","href":"/api/classes/thatopen_components.Component","docId":"api/classes/thatopen_components.Component"},{"type":"link","label":"Components","href":"/api/classes/thatopen_components.Components","docId":"api/classes/thatopen_components.Components"},{"type":"link","label":"CullerRenderer","href":"/api/classes/thatopen_components.CullerRenderer","docId":"api/classes/thatopen_components.CullerRenderer"},{"type":"link","label":"Cullers","href":"/api/classes/thatopen_components.Cullers","docId":"api/classes/thatopen_components.Cullers"},{"type":"link","label":"Disposer","href":"/api/classes/thatopen_components.Disposer","docId":"api/classes/thatopen_components.Disposer"},{"type":"link","label":"Event","href":"/api/classes/thatopen_components.Event","docId":"api/classes/thatopen_components.Event"},{"type":"link","label":"FirstPersonMode","href":"/api/classes/thatopen_components.FirstPersonMode","docId":"api/classes/thatopen_components.FirstPersonMode"},{"type":"link","label":"FragmentsManager","href":"/api/classes/thatopen_components.FragmentsManager","docId":"api/classes/thatopen_components.FragmentsManager"},{"type":"link","label":"IfcJsonExporter","href":"/api/classes/thatopen_components.IfcJsonExporter","docId":"api/classes/thatopen_components.IfcJsonExporter"},{"type":"link","label":"IfcRelationsIndexer","href":"/api/classes/thatopen_components.IfcRelationsIndexer","docId":"api/classes/thatopen_components.IfcRelationsIndexer"},{"type":"link","label":"IfcStreamingSettings","href":"/api/classes/thatopen_components.IfcStreamingSettings","docId":"api/classes/thatopen_components.IfcStreamingSettings"},{"type":"link","label":"MeshCullerRenderer","href":"/api/classes/thatopen_components.MeshCullerRenderer","docId":"api/classes/thatopen_components.MeshCullerRenderer"},{"type":"link","label":"OrbitMode","href":"/api/classes/thatopen_components.OrbitMode","docId":"api/classes/thatopen_components.OrbitMode"},{"type":"link","label":"OrthoPerspectiveCamera","href":"/api/classes/thatopen_components.OrthoPerspectiveCamera","docId":"api/classes/thatopen_components.OrthoPerspectiveCamera"},{"type":"link","label":"PlanMode","href":"/api/classes/thatopen_components.PlanMode","docId":"api/classes/thatopen_components.PlanMode"},{"type":"link","label":"ProjectionManager","href":"/api/classes/thatopen_components.ProjectionManager","docId":"api/classes/thatopen_components.ProjectionManager"},{"type":"link","label":"PropertiesStreamingSettings","href":"/api/classes/thatopen_components.PropertiesStreamingSettings","docId":"api/classes/thatopen_components.PropertiesStreamingSettings"},{"type":"link","label":"SimpleCamera","href":"/api/classes/thatopen_components.SimpleCamera","docId":"api/classes/thatopen_components.SimpleCamera"},{"type":"link","label":"SimplePlane","href":"/api/classes/thatopen_components.SimplePlane","docId":"api/classes/thatopen_components.SimplePlane"},{"type":"link","label":"SimpleRenderer","href":"/api/classes/thatopen_components.SimpleRenderer","docId":"api/classes/thatopen_components.SimpleRenderer"},{"type":"link","label":"SimpleScene","href":"/api/classes/thatopen_components.SimpleScene","docId":"api/classes/thatopen_components.SimpleScene"},{"type":"link","label":"ClipEdges","href":"/api/classes/thatopen_components_front.ClipEdges","docId":"api/classes/thatopen_components_front.ClipEdges"},{"type":"link","label":"EdgesPlane","href":"/api/classes/thatopen_components_front.EdgesPlane","docId":"api/classes/thatopen_components_front.EdgesPlane"},{"type":"link","label":"LengthMeasurement","href":"/api/classes/thatopen_components_front.LengthMeasurement","docId":"api/classes/thatopen_components_front.LengthMeasurement"},{"type":"link","label":"Marker","href":"/api/classes/thatopen_components_front.Marker","docId":"api/classes/thatopen_components_front.Marker"},{"type":"link","label":"Plans","href":"/api/classes/thatopen_components_front.Plans","docId":"api/classes/thatopen_components_front.Plans"},{"type":"link","label":"PostproductionRenderer","href":"/api/classes/thatopen_components_front.PostproductionRenderer","docId":"api/classes/thatopen_components_front.PostproductionRenderer"},{"type":"link","label":"RendererWith2D","href":"/api/classes/thatopen_components_front.RendererWith2D","docId":"api/classes/thatopen_components_front.RendererWith2D"},{"type":"link","label":"Serializer","href":"/api/classes/thatopen_fragments.Serializer","docId":"api/classes/thatopen_fragments.Serializer"},{"type":"link","label":"Button","href":"/api/classes/thatopen_ui.Button","docId":"api/classes/thatopen_ui.Button"},{"type":"link","label":"Checkbox","href":"/api/classes/thatopen_ui.Checkbox","docId":"api/classes/thatopen_ui.Checkbox"},{"type":"link","label":"ColorInput","href":"/api/classes/thatopen_ui.ColorInput","docId":"api/classes/thatopen_ui.ColorInput"},{"type":"link","label":"Component","href":"/api/classes/thatopen_ui.Component","docId":"api/classes/thatopen_ui.Component"},{"type":"link","label":"ContextMenu","href":"/api/classes/thatopen_ui.ContextMenu","docId":"api/classes/thatopen_ui.ContextMenu"},{"type":"link","label":"Dropdown","href":"/api/classes/thatopen_ui.Dropdown","docId":"api/classes/thatopen_ui.Dropdown"},{"type":"link","label":"Grid","href":"/api/classes/thatopen_ui.Grid","docId":"api/classes/thatopen_ui.Grid"},{"type":"link","label":"Icon","href":"/api/classes/thatopen_ui.Icon","docId":"api/classes/thatopen_ui.Icon"},{"type":"link","label":"Input","href":"/api/classes/thatopen_ui.Input","docId":"api/classes/thatopen_ui.Input"},{"type":"link","label":"Label","href":"/api/classes/thatopen_ui.Label","docId":"api/classes/thatopen_ui.Label"},{"type":"link","label":"NumberInput","href":"/api/classes/thatopen_ui.NumberInput","docId":"api/classes/thatopen_ui.NumberInput"},{"type":"link","label":"Option","href":"/api/classes/thatopen_ui.Option","docId":"api/classes/thatopen_ui.Option"},{"type":"link","label":"Panel","href":"/api/classes/thatopen_ui.Panel","docId":"api/classes/thatopen_ui.Panel"},{"type":"link","label":"PanelSection","href":"/api/classes/thatopen_ui.PanelSection","docId":"api/classes/thatopen_ui.PanelSection"},{"type":"link","label":"Selector","href":"/api/classes/thatopen_ui.Selector","docId":"api/classes/thatopen_ui.Selector"},{"type":"link","label":"Tab","href":"/api/classes/thatopen_ui.Tab","docId":"api/classes/thatopen_ui.Tab"},{"type":"link","label":"Table","href":"/api/classes/thatopen_ui.Table","docId":"api/classes/thatopen_ui.Table"},{"type":"link","label":"Tabs","href":"/api/classes/thatopen_ui.Tabs","docId":"api/classes/thatopen_ui.Tabs"},{"type":"link","label":"TextInput","href":"/api/classes/thatopen_ui.TextInput","docId":"api/classes/thatopen_ui.TextInput"},{"type":"link","label":"Toolbar","href":"/api/classes/thatopen_ui.Toolbar","docId":"api/classes/thatopen_ui.Toolbar"},{"type":"link","label":"ToolbarGroup","href":"/api/classes/thatopen_ui.ToolbarGroup","docId":"api/classes/thatopen_ui.ToolbarGroup"},{"type":"link","label":"ToolbarSection","href":"/api/classes/thatopen_ui.ToolbarSection","docId":"api/classes/thatopen_ui.ToolbarSection"},{"type":"link","label":"Viewport","href":"/api/classes/thatopen_ui.Viewport","docId":"api/classes/thatopen_ui.Viewport"},{"type":"link","label":"Manager","href":"/api/classes/thatopen_ui_obc.Manager","docId":"api/classes/thatopen_ui_obc.Manager"},{"type":"link","label":"ViewCube","href":"/api/classes/thatopen_ui_obc.ViewCube","docId":"api/classes/thatopen_ui_obc.ViewCube"},{"type":"link","label":"World2D","href":"/api/classes/thatopen_ui_obc.World2D","docId":"api/classes/thatopen_ui_obc.World2D"}]},{"type":"category","label":"Interfaces","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"BVHGeometry","href":"/api/interfaces/thatopen_components.BVHGeometry","docId":"api/interfaces/thatopen_components.BVHGeometry"},{"type":"link","label":"CameraControllable","href":"/api/interfaces/thatopen_components.CameraControllable","docId":"api/interfaces/thatopen_components.CameraControllable"},{"type":"link","label":"Configurable","href":"/api/interfaces/thatopen_components.Configurable","docId":"api/interfaces/thatopen_components.Configurable"},{"type":"link","label":"Createable","href":"/api/interfaces/thatopen_components.Createable","docId":"api/interfaces/thatopen_components.Createable"},{"type":"link","label":"Disposable","href":"/api/interfaces/thatopen_components.Disposable","docId":"api/interfaces/thatopen_components.Disposable"},{"type":"link","label":"Hideable","href":"/api/interfaces/thatopen_components.Hideable","docId":"api/interfaces/thatopen_components.Hideable"},{"type":"link","label":"NavigationMode","href":"/api/interfaces/thatopen_components.NavigationMode","docId":"api/interfaces/thatopen_components.NavigationMode"},{"type":"link","label":"Progress","href":"/api/interfaces/thatopen_components.Progress","docId":"api/interfaces/thatopen_components.Progress"},{"type":"link","label":"Resizeable","href":"/api/interfaces/thatopen_components.Resizeable","docId":"api/interfaces/thatopen_components.Resizeable"},{"type":"link","label":"Updateable","href":"/api/interfaces/thatopen_components.Updateable","docId":"api/interfaces/thatopen_components.Updateable"},{"type":"link","label":"ColumnData","href":"/api/interfaces/thatopen_ui.ColumnData","docId":"api/interfaces/thatopen_ui.ColumnData"},{"type":"link","label":"EntryQuery","href":"/api/interfaces/thatopen_ui.EntryQuery","docId":"api/interfaces/thatopen_ui.EntryQuery"},{"type":"link","label":"HasName","href":"/api/interfaces/thatopen_ui.HasName","docId":"api/interfaces/thatopen_ui.HasName"},{"type":"link","label":"HasValue","href":"/api/interfaces/thatopen_ui.HasValue","docId":"api/interfaces/thatopen_ui.HasValue"},{"type":"link","label":"QueryGroup","href":"/api/interfaces/thatopen_ui.QueryGroup","docId":"api/interfaces/thatopen_ui.QueryGroup"}]}],"href":"/api/"},{"type":"category","label":"Tutorials","collapsible":true,"collapsed":true,"items":[{"type":"category","label":"Components","collapsible":true,"collapsed":true,"items":[{"type":"category","label":"Core","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"BoundingBoxer","href":"/Tutorials/Components/Core/BoundingBoxer","docId":"Tutorials/Components/Core/BoundingBoxer"},{"type":"link","label":"Clipper","href":"/Tutorials/Components/Core/Clipper","docId":"Tutorials/Components/Core/Clipper"},{"type":"link","label":"Cullers","href":"/Tutorials/Components/Core/Cullers","docId":"Tutorials/Components/Core/Cullers"},{"type":"link","label":"FragmentsManager","href":"/Tutorials/Components/Core/FragmentsManager","docId":"Tutorials/Components/Core/FragmentsManager"},{"type":"link","label":"Grids","href":"/Tutorials/Components/Core/Grids","docId":"Tutorials/Components/Core/Grids"},{"type":"link","label":"Hider","href":"/Tutorials/Components/Core/Hider","docId":"Tutorials/Components/Core/Hider"},{"type":"link","label":"IfcGeometryTiler","href":"/Tutorials/Components/Core/IfcGeometryTiler","docId":"Tutorials/Components/Core/IfcGeometryTiler"},{"type":"link","label":"IfcLoader","href":"/Tutorials/Components/Core/IfcLoader","docId":"Tutorials/Components/Core/IfcLoader"},{"type":"link","label":"IfcPropertiesTiler","href":"/Tutorials/Components/Core/IfcPropertiesTiler","docId":"Tutorials/Components/Core/IfcPropertiesTiler"},{"type":"link","label":"IfcRelationsIndexer","href":"/Tutorials/Components/Core/IfcRelationsIndexer","docId":"Tutorials/Components/Core/IfcRelationsIndexer"},{"type":"link","label":"MiniMap","href":"/Tutorials/Components/Core/MiniMap","docId":"Tutorials/Components/Core/MiniMap"},{"type":"link","label":"OrthoPerspectiveCamera","href":"/Tutorials/Components/Core/OrthoPerspectiveCamera","docId":"Tutorials/Components/Core/OrthoPerspectiveCamera"},{"type":"link","label":"Raycasters","href":"/Tutorials/Components/Core/Raycasters","docId":"Tutorials/Components/Core/Raycasters"},{"type":"link","label":"Worlds","href":"/Tutorials/Components/Core/Worlds","docId":"Tutorials/Components/Core/Worlds"}]},{"type":"category","label":"Front","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"AngleMeasurement","href":"/Tutorials/Components/Front/AngleMeasurement","docId":"Tutorials/Components/Front/AngleMeasurement"},{"type":"link","label":"AreaMeasurement","href":"/Tutorials/Components/Front/AreaMeasurement","docId":"Tutorials/Components/Front/AreaMeasurement"},{"type":"link","label":"EdgesClipper","href":"/Tutorials/Components/Front/EdgesClipper","docId":"Tutorials/Components/Front/EdgesClipper"},{"type":"link","label":"IfcStreamer","href":"/Tutorials/Components/Front/IfcStreamer","docId":"Tutorials/Components/Front/IfcStreamer"},{"type":"link","label":"LengthMeasurement","href":"/Tutorials/Components/Front/LengthMeasurement","docId":"Tutorials/Components/Front/LengthMeasurement"},{"type":"link","label":"PostproductionRenderer","href":"/Tutorials/Components/Front/PostproductionRenderer","docId":"Tutorials/Components/Front/PostproductionRenderer"},{"type":"link","label":"ShadowDropper","href":"/Tutorials/Components/Front/ShadowDropper","docId":"Tutorials/Components/Front/ShadowDropper"}]}],"href":"/Tutorials/Components/"},{"type":"category","label":"UserInterface","collapsible":true,"collapsed":true,"items":[{"type":"category","label":"Core","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"Component","href":"/Tutorials/UserInterface/Core/Component","docId":"Tutorials/UserInterface/Core/Component"}]},{"type":"category","label":"OBC","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"ClassificationsTree","href":"/Tutorials/UserInterface/OBC/ClassificationsTree","docId":"Tutorials/UserInterface/OBC/ClassificationsTree"},{"type":"link","label":"ElementProperties","href":"/Tutorials/UserInterface/OBC/ElementProperties","docId":"Tutorials/UserInterface/OBC/ElementProperties"},{"type":"link","label":"EntityAttributes","href":"/Tutorials/UserInterface/OBC/EntityAttributes","docId":"Tutorials/UserInterface/OBC/EntityAttributes"},{"type":"link","label":"ModelsList","href":"/Tutorials/UserInterface/OBC/ModelsList","docId":"Tutorials/UserInterface/OBC/ModelsList"}]}],"href":"/Tutorials/UserInterface/"}]}]},"docs":{"api/classes/thatopen_components_front.ClipEdges":{"id":"api/classes/thatopen_components_front.ClipEdges","title":"Class: ClipEdges","description":"@thatopen/components-front.ClipEdges","sidebar":"tutorialSidebar"},"api/classes/thatopen_components_front.EdgesPlane":{"id":"api/classes/thatopen_components_front.EdgesPlane","title":"Class: EdgesPlane","description":"@thatopen/components-front.EdgesPlane","sidebar":"tutorialSidebar"},"api/classes/thatopen_components_front.LengthMeasurement":{"id":"api/classes/thatopen_components_front.LengthMeasurement","title":"Class: LengthMeasurement","description":"@thatopen/components-front.LengthMeasurement","sidebar":"tutorialSidebar"},"api/classes/thatopen_components_front.Marker":{"id":"api/classes/thatopen_components_front.Marker","title":"Class: Marker","description":"@thatopen/components-front.Marker","sidebar":"tutorialSidebar"},"api/classes/thatopen_components_front.Plans":{"id":"api/classes/thatopen_components_front.Plans","title":"Class: Plans","description":"@thatopen/components-front.Plans","sidebar":"tutorialSidebar"},"api/classes/thatopen_components_front.PostproductionRenderer":{"id":"api/classes/thatopen_components_front.PostproductionRenderer","title":"Class: PostproductionRenderer","description":"@thatopen/components-front.PostproductionRenderer","sidebar":"tutorialSidebar"},"api/classes/thatopen_components_front.RendererWith2D":{"id":"api/classes/thatopen_components_front.RendererWith2D","title":"Class: RendererWith2D","description":"@thatopen/components-front.RendererWith2D","sidebar":"tutorialSidebar"},"api/classes/thatopen_components.AsyncEvent":{"id":"api/classes/thatopen_components.AsyncEvent","title":"Class: AsyncEvent","description":"@thatopen/components.AsyncEvent","sidebar":"tutorialSidebar"},"api/classes/thatopen_components.Base":{"id":"api/classes/thatopen_components.Base","title":"Class: Base","description":"@thatopen/components.Base","sidebar":"tutorialSidebar"},"api/classes/thatopen_components.BaseWorldItem":{"id":"api/classes/thatopen_components.BaseWorldItem","title":"Class: BaseWorldItem","description":"@thatopen/components.BaseWorldItem","sidebar":"tutorialSidebar"},"api/classes/thatopen_components.BoundingBoxer":{"id":"api/classes/thatopen_components.BoundingBoxer","title":"Class: BoundingBoxer","description":"@thatopen/components.BoundingBoxer","sidebar":"tutorialSidebar"},"api/classes/thatopen_components.Clipper":{"id":"api/classes/thatopen_components.Clipper","title":"Class: Clipper","description":"@thatopen/components.Clipper","sidebar":"tutorialSidebar"},"api/classes/thatopen_components.Component":{"id":"api/classes/thatopen_components.Component","title":"Class: Component","description":"@thatopen/components.Component","sidebar":"tutorialSidebar"},"api/classes/thatopen_components.Components":{"id":"api/classes/thatopen_components.Components","title":"Class: Components","description":"@thatopen/components.Components","sidebar":"tutorialSidebar"},"api/classes/thatopen_components.CullerRenderer":{"id":"api/classes/thatopen_components.CullerRenderer","title":"Class: CullerRenderer","description":"@thatopen/components.CullerRenderer","sidebar":"tutorialSidebar"},"api/classes/thatopen_components.Cullers":{"id":"api/classes/thatopen_components.Cullers","title":"Class: Cullers","description":"@thatopen/components.Cullers","sidebar":"tutorialSidebar"},"api/classes/thatopen_components.Disposer":{"id":"api/classes/thatopen_components.Disposer","title":"Class: Disposer","description":"@thatopen/components.Disposer","sidebar":"tutorialSidebar"},"api/classes/thatopen_components.Event":{"id":"api/classes/thatopen_components.Event","title":"Class: Event","description":"@thatopen/components.Event","sidebar":"tutorialSidebar"},"api/classes/thatopen_components.FirstPersonMode":{"id":"api/classes/thatopen_components.FirstPersonMode","title":"Class: FirstPersonMode","description":"@thatopen/components.FirstPersonMode","sidebar":"tutorialSidebar"},"api/classes/thatopen_components.FragmentsManager":{"id":"api/classes/thatopen_components.FragmentsManager","title":"Class: FragmentsManager","description":"@thatopen/components.FragmentsManager","sidebar":"tutorialSidebar"},"api/classes/thatopen_components.IfcJsonExporter":{"id":"api/classes/thatopen_components.IfcJsonExporter","title":"Class: IfcJsonExporter","description":"@thatopen/components.IfcJsonExporter","sidebar":"tutorialSidebar"},"api/classes/thatopen_components.IfcRelationsIndexer":{"id":"api/classes/thatopen_components.IfcRelationsIndexer","title":"Class: IfcRelationsIndexer","description":"@thatopen/components.IfcRelationsIndexer","sidebar":"tutorialSidebar"},"api/classes/thatopen_components.IfcStreamingSettings":{"id":"api/classes/thatopen_components.IfcStreamingSettings","title":"Class: IfcStreamingSettings","description":"@thatopen/components.IfcStreamingSettings","sidebar":"tutorialSidebar"},"api/classes/thatopen_components.MeshCullerRenderer":{"id":"api/classes/thatopen_components.MeshCullerRenderer","title":"Class: MeshCullerRenderer","description":"@thatopen/components.MeshCullerRenderer","sidebar":"tutorialSidebar"},"api/classes/thatopen_components.OrbitMode":{"id":"api/classes/thatopen_components.OrbitMode","title":"Class: OrbitMode","description":"@thatopen/components.OrbitMode","sidebar":"tutorialSidebar"},"api/classes/thatopen_components.OrthoPerspectiveCamera":{"id":"api/classes/thatopen_components.OrthoPerspectiveCamera","title":"Class: OrthoPerspectiveCamera","description":"@thatopen/components.OrthoPerspectiveCamera","sidebar":"tutorialSidebar"},"api/classes/thatopen_components.PlanMode":{"id":"api/classes/thatopen_components.PlanMode","title":"Class: PlanMode","description":"@thatopen/components.PlanMode","sidebar":"tutorialSidebar"},"api/classes/thatopen_components.ProjectionManager":{"id":"api/classes/thatopen_components.ProjectionManager","title":"Class: ProjectionManager","description":"@thatopen/components.ProjectionManager","sidebar":"tutorialSidebar"},"api/classes/thatopen_components.PropertiesStreamingSettings":{"id":"api/classes/thatopen_components.PropertiesStreamingSettings","title":"Class: PropertiesStreamingSettings","description":"@thatopen/components.PropertiesStreamingSettings","sidebar":"tutorialSidebar"},"api/classes/thatopen_components.SimpleCamera":{"id":"api/classes/thatopen_components.SimpleCamera","title":"Class: SimpleCamera","description":"@thatopen/components.SimpleCamera","sidebar":"tutorialSidebar"},"api/classes/thatopen_components.SimplePlane":{"id":"api/classes/thatopen_components.SimplePlane","title":"Class: SimplePlane","description":"@thatopen/components.SimplePlane","sidebar":"tutorialSidebar"},"api/classes/thatopen_components.SimpleRenderer":{"id":"api/classes/thatopen_components.SimpleRenderer","title":"Class: SimpleRenderer","description":"@thatopen/components.SimpleRenderer","sidebar":"tutorialSidebar"},"api/classes/thatopen_components.SimpleScene":{"id":"api/classes/thatopen_components.SimpleScene","title":"Class: SimpleScene","description":"@thatopen/components.SimpleScene","sidebar":"tutorialSidebar"},"api/classes/thatopen_fragments.Serializer":{"id":"api/classes/thatopen_fragments.Serializer","title":"Class: Serializer","description":"@thatopen/fragments.Serializer","sidebar":"tutorialSidebar"},"api/classes/thatopen_ui_obc.Manager":{"id":"api/classes/thatopen_ui_obc.Manager","title":"Class: Manager","description":"@thatopen/ui-obc.Manager","sidebar":"tutorialSidebar"},"api/classes/thatopen_ui_obc.ViewCube":{"id":"api/classes/thatopen_ui_obc.ViewCube","title":"Class: ViewCube","description":"@thatopen/ui-obc.ViewCube","sidebar":"tutorialSidebar"},"api/classes/thatopen_ui_obc.World2D":{"id":"api/classes/thatopen_ui_obc.World2D","title":"Class: World2D","description":"@thatopen/ui-obc.World2D","sidebar":"tutorialSidebar"},"api/classes/thatopen_ui.Button":{"id":"api/classes/thatopen_ui.Button","title":"Class: Button","description":"@thatopen/ui.Button","sidebar":"tutorialSidebar"},"api/classes/thatopen_ui.Checkbox":{"id":"api/classes/thatopen_ui.Checkbox","title":"Class: Checkbox","description":"@thatopen/ui.Checkbox","sidebar":"tutorialSidebar"},"api/classes/thatopen_ui.ColorInput":{"id":"api/classes/thatopen_ui.ColorInput","title":"Class: ColorInput","description":"@thatopen/ui.ColorInput","sidebar":"tutorialSidebar"},"api/classes/thatopen_ui.Component":{"id":"api/classes/thatopen_ui.Component","title":"Class: Component","description":"@thatopen/ui.Component","sidebar":"tutorialSidebar"},"api/classes/thatopen_ui.ContextMenu":{"id":"api/classes/thatopen_ui.ContextMenu","title":"Class: ContextMenu","description":"@thatopen/ui.ContextMenu","sidebar":"tutorialSidebar"},"api/classes/thatopen_ui.Dropdown":{"id":"api/classes/thatopen_ui.Dropdown","title":"Class: Dropdown","description":"@thatopen/ui.Dropdown","sidebar":"tutorialSidebar"},"api/classes/thatopen_ui.Grid":{"id":"api/classes/thatopen_ui.Grid","title":"Class: Grid","description":"@thatopen/ui.Grid","sidebar":"tutorialSidebar"},"api/classes/thatopen_ui.Icon":{"id":"api/classes/thatopen_ui.Icon","title":"Class: Icon","description":"@thatopen/ui.Icon","sidebar":"tutorialSidebar"},"api/classes/thatopen_ui.Input":{"id":"api/classes/thatopen_ui.Input","title":"Class: Input","description":"@thatopen/ui.Input","sidebar":"tutorialSidebar"},"api/classes/thatopen_ui.Label":{"id":"api/classes/thatopen_ui.Label","title":"Class: Label","description":"@thatopen/ui.Label","sidebar":"tutorialSidebar"},"api/classes/thatopen_ui.NumberInput":{"id":"api/classes/thatopen_ui.NumberInput","title":"Class: NumberInput","description":"@thatopen/ui.NumberInput","sidebar":"tutorialSidebar"},"api/classes/thatopen_ui.Option":{"id":"api/classes/thatopen_ui.Option","title":"Class: Option","description":"@thatopen/ui.Option","sidebar":"tutorialSidebar"},"api/classes/thatopen_ui.Panel":{"id":"api/classes/thatopen_ui.Panel","title":"Class: Panel","description":"@thatopen/ui.Panel","sidebar":"tutorialSidebar"},"api/classes/thatopen_ui.PanelSection":{"id":"api/classes/thatopen_ui.PanelSection","title":"Class: PanelSection","description":"@thatopen/ui.PanelSection","sidebar":"tutorialSidebar"},"api/classes/thatopen_ui.Selector":{"id":"api/classes/thatopen_ui.Selector","title":"Class: Selector","description":"@thatopen/ui.Selector","sidebar":"tutorialSidebar"},"api/classes/thatopen_ui.Tab":{"id":"api/classes/thatopen_ui.Tab","title":"Class: Tab","description":"@thatopen/ui.Tab","sidebar":"tutorialSidebar"},"api/classes/thatopen_ui.Table":{"id":"api/classes/thatopen_ui.Table","title":"Class: Table","description":"@thatopen/ui.Table","sidebar":"tutorialSidebar"},"api/classes/thatopen_ui.Tabs":{"id":"api/classes/thatopen_ui.Tabs","title":"Class: Tabs","description":"@thatopen/ui.Tabs","sidebar":"tutorialSidebar"},"api/classes/thatopen_ui.TextInput":{"id":"api/classes/thatopen_ui.TextInput","title":"Class: TextInput","description":"@thatopen/ui.TextInput","sidebar":"tutorialSidebar"},"api/classes/thatopen_ui.Toolbar":{"id":"api/classes/thatopen_ui.Toolbar","title":"Class: Toolbar","description":"@thatopen/ui.Toolbar","sidebar":"tutorialSidebar"},"api/classes/thatopen_ui.ToolbarGroup":{"id":"api/classes/thatopen_ui.ToolbarGroup","title":"Class: ToolbarGroup","description":"@thatopen/ui.ToolbarGroup","sidebar":"tutorialSidebar"},"api/classes/thatopen_ui.ToolbarSection":{"id":"api/classes/thatopen_ui.ToolbarSection","title":"Class: ToolbarSection","description":"@thatopen/ui.ToolbarSection","sidebar":"tutorialSidebar"},"api/classes/thatopen_ui.Viewport":{"id":"api/classes/thatopen_ui.Viewport","title":"Class: Viewport","description":"@thatopen/ui.Viewport","sidebar":"tutorialSidebar"},"api/index":{"id":"api/index","title":"Open BIM Docs","description":"TOC","sidebar":"tutorialSidebar"},"api/interfaces/thatopen_components.BVHGeometry":{"id":"api/interfaces/thatopen_components.BVHGeometry","title":"Interface: BVHGeometry","description":"@thatopen/components.BVHGeometry","sidebar":"tutorialSidebar"},"api/interfaces/thatopen_components.CameraControllable":{"id":"api/interfaces/thatopen_components.CameraControllable","title":"Interface: CameraControllable","description":"@thatopen/components.CameraControllable","sidebar":"tutorialSidebar"},"api/interfaces/thatopen_components.Configurable":{"id":"api/interfaces/thatopen_components.Configurable","title":"Interface: Configurable","description":"@thatopen/components.Configurable","sidebar":"tutorialSidebar"},"api/interfaces/thatopen_components.Createable":{"id":"api/interfaces/thatopen_components.Createable","title":"Interface: Createable","description":"@thatopen/components.Createable","sidebar":"tutorialSidebar"},"api/interfaces/thatopen_components.Disposable":{"id":"api/interfaces/thatopen_components.Disposable","title":"Interface: Disposable","description":"@thatopen/components.Disposable","sidebar":"tutorialSidebar"},"api/interfaces/thatopen_components.Hideable":{"id":"api/interfaces/thatopen_components.Hideable","title":"Interface: Hideable","description":"@thatopen/components.Hideable","sidebar":"tutorialSidebar"},"api/interfaces/thatopen_components.NavigationMode":{"id":"api/interfaces/thatopen_components.NavigationMode","title":"Interface: NavigationMode","description":"@thatopen/components.NavigationMode","sidebar":"tutorialSidebar"},"api/interfaces/thatopen_components.Progress":{"id":"api/interfaces/thatopen_components.Progress","title":"Interface: Progress","description":"@thatopen/components.Progress","sidebar":"tutorialSidebar"},"api/interfaces/thatopen_components.Resizeable":{"id":"api/interfaces/thatopen_components.Resizeable","title":"Interface: Resizeable","description":"@thatopen/components.Resizeable","sidebar":"tutorialSidebar"},"api/interfaces/thatopen_components.Updateable":{"id":"api/interfaces/thatopen_components.Updateable","title":"Interface: Updateable","description":"@thatopen/components.Updateable","sidebar":"tutorialSidebar"},"api/interfaces/thatopen_ui.ColumnData":{"id":"api/interfaces/thatopen_ui.ColumnData","title":"Interface: ColumnData","description":"@thatopen/ui.ColumnData","sidebar":"tutorialSidebar"},"api/interfaces/thatopen_ui.EntryQuery":{"id":"api/interfaces/thatopen_ui.EntryQuery","title":"Interface: EntryQuery","description":"@thatopen/ui.EntryQuery","sidebar":"tutorialSidebar"},"api/interfaces/thatopen_ui.HasName":{"id":"api/interfaces/thatopen_ui.HasName","title":"Interface: HasName","description":"@thatopen/ui.HasName","sidebar":"tutorialSidebar"},"api/interfaces/thatopen_ui.HasValue":{"id":"api/interfaces/thatopen_ui.HasValue","title":"Interface: HasValue","description":"@thatopen/ui.HasValue","sidebar":"tutorialSidebar"},"api/interfaces/thatopen_ui.QueryGroup":{"id":"api/interfaces/thatopen_ui.QueryGroup","title":"Interface: QueryGroup","description":"@thatopen/ui.QueryGroup","sidebar":"tutorialSidebar"},"api/modules":{"id":"api/modules","title":"Open BIM Docs","description":"","sidebar":"tutorialSidebar"},"api/modules/thatopen_components":{"id":"api/modules/thatopen_components","title":"Module: @thatopen/components","description":"Classes","sidebar":"tutorialSidebar"},"api/modules/thatopen_components_front":{"id":"api/modules/thatopen_components_front","title":"Module: @thatopen/components-front","description":"Classes","sidebar":"tutorialSidebar"},"api/modules/thatopen_fragments":{"id":"api/modules/thatopen_fragments","title":"Module: @thatopen/fragments","description":"Classes","sidebar":"tutorialSidebar"},"api/modules/thatopen_ui":{"id":"api/modules/thatopen_ui","title":"Module: @thatopen/ui","description":"Classes","sidebar":"tutorialSidebar"},"api/modules/thatopen_ui_obc":{"id":"api/modules/thatopen_ui_obc","title":"Module: @thatopen/ui-obc","description":"Classes","sidebar":"tutorialSidebar"},"components/clean-components-guide":{"id":"components/clean-components-guide","title":"\ud83e\uddf9 Keeping them clean","description":"\ud83e\uddfd Basics","sidebar":"tutorialSidebar"},"components/creating-components":{"id":"components/creating-components","title":"\ud83e\uddbe Making your own","description":"\ud83d\udc6a Create it","sidebar":"tutorialSidebar"},"components/getting-started":{"id":"components/getting-started","title":"\ud83d\ude80 Getting started","description":"\ud83d\udc69\ud83c\udffb\u200d\ud83c\udfeb Component ABC","sidebar":"tutorialSidebar"},"components/tutorial-paths":{"id":"components/tutorial-paths","title":"\ud83e\udded Tutorial paths","description":"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! \ud83d\udd25\ud83d\udd25\ud83d\ude80","sidebar":"tutorialSidebar"},"contributing":{"id":"contributing","title":"\ud83e\udd1d Get involved","description":"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. \ud83d\udc47\ud83c\udffb","sidebar":"tutorialSidebar"},"intro":{"id":"intro","title":"\ud83d\udc68\ud83c\udffb\u200d\ud83d\udcbb Introduction","description":"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! \ud83c\udfe2\ud83d\udc69\u200d\ud83d\udcbb","sidebar":"tutorialSidebar"},"Tutorials/Components/Core/BoundingBoxer":{"id":"Tutorials/Components/Core/BoundingBoxer","title":"BoundingBoxer","description":"Copying and pasting? We\'ve got you covered! You can find the full source code of this tutorial here.","sidebar":"tutorialSidebar"},"Tutorials/Components/Core/Clipper":{"id":"Tutorials/Components/Core/Clipper","title":"Clipper","description":"Copying and pasting? We\'ve got you covered! You can find the full source code of this tutorial here.","sidebar":"tutorialSidebar"},"Tutorials/Components/Core/Cullers":{"id":"Tutorials/Components/Core/Cullers","title":"Cullers","description":"Copying and pasting? We\'ve got you covered! You can find the full source code of this tutorial here.","sidebar":"tutorialSidebar"},"Tutorials/Components/Core/FragmentsManager":{"id":"Tutorials/Components/Core/FragmentsManager","title":"FragmentsManager","description":"Copying and pasting? We\'ve got you covered! You can find the full source code of this tutorial here.","sidebar":"tutorialSidebar"},"Tutorials/Components/Core/Grids":{"id":"Tutorials/Components/Core/Grids","title":"Grids","description":"Copying and pasting? We\'ve got you covered! You can find the full source code of this tutorial here.","sidebar":"tutorialSidebar"},"Tutorials/Components/Core/Hider":{"id":"Tutorials/Components/Core/Hider","title":"Hider","description":"Copying and pasting? We\'ve got you covered! You can find the full source code of this tutorial here.","sidebar":"tutorialSidebar"},"Tutorials/Components/Core/IfcGeometryTiler":{"id":"Tutorials/Components/Core/IfcGeometryTiler","title":"IfcGeometryTiler","description":"Copying and pasting? We\'ve got you covered! You can find the full source code of this tutorial here.","sidebar":"tutorialSidebar"},"Tutorials/Components/Core/IfcLoader":{"id":"Tutorials/Components/Core/IfcLoader","title":"IfcLoader","description":"Copying and pasting? We\'ve got you covered! You can find the full source code of this tutorial here.","sidebar":"tutorialSidebar"},"Tutorials/Components/Core/IfcPropertiesTiler":{"id":"Tutorials/Components/Core/IfcPropertiesTiler","title":"IfcPropertiesTiler","description":"Copying and pasting? We\'ve got you covered! You can find the full source code of this tutorial here.","sidebar":"tutorialSidebar"},"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.","sidebar":"tutorialSidebar"},"Tutorials/Components/Core/MiniMap":{"id":"Tutorials/Components/Core/MiniMap","title":"MiniMap","description":"Copying and pasting? We\'ve got you covered! You can find the full source code of this tutorial here.","sidebar":"tutorialSidebar"},"Tutorials/Components/Core/OrthoPerspectiveCamera":{"id":"Tutorials/Components/Core/OrthoPerspectiveCamera","title":"OrthoPerspectiveCamera","description":"Copying and pasting? We\'ve got you covered! You can find the full source code of this tutorial here.","sidebar":"tutorialSidebar"},"Tutorials/Components/Core/Raycasters":{"id":"Tutorials/Components/Core/Raycasters","title":"Raycasters","description":"Copying and pasting? We\'ve got you covered! You can find the full source code of this tutorial here.","sidebar":"tutorialSidebar"},"Tutorials/Components/Core/Worlds":{"id":"Tutorials/Components/Core/Worlds","title":"Worlds","description":"Copying and pasting? We\'ve got you covered! You can find the full source code of this tutorial here.","sidebar":"tutorialSidebar"},"Tutorials/Components/Front/AngleMeasurement":{"id":"Tutorials/Components/Front/AngleMeasurement","title":"AngleMeasurement","description":"Copying and pasting? We\'ve got you covered! You can find the full source code of this tutorial here.","sidebar":"tutorialSidebar"},"Tutorials/Components/Front/AreaMeasurement":{"id":"Tutorials/Components/Front/AreaMeasurement","title":"AreaMeasurement","description":"Copying and pasting? We\'ve got you covered! You can find the full source code of this tutorial here.","sidebar":"tutorialSidebar"},"Tutorials/Components/Front/EdgesClipper":{"id":"Tutorials/Components/Front/EdgesClipper","title":"EdgesClipper","description":"Copying and pasting? We\'ve got you covered! You can find the full source code of this tutorial here.","sidebar":"tutorialSidebar"},"Tutorials/Components/Front/IfcStreamer":{"id":"Tutorials/Components/Front/IfcStreamer","title":"IfcStreamer","description":"Copying and pasting? We\'ve got you covered! You can find the full source code of this tutorial here.","sidebar":"tutorialSidebar"},"Tutorials/Components/Front/LengthMeasurement":{"id":"Tutorials/Components/Front/LengthMeasurement","title":"LengthMeasurement","description":"Copying and pasting? We\'ve got you covered! You can find the full source code of this tutorial here.","sidebar":"tutorialSidebar"},"Tutorials/Components/Front/PostproductionRenderer":{"id":"Tutorials/Components/Front/PostproductionRenderer","title":"PostproductionRenderer","description":"Copying and pasting? We\'ve got you covered! You can find the full source code of this tutorial here.","sidebar":"tutorialSidebar"},"Tutorials/Components/Front/ShadowDropper":{"id":"Tutorials/Components/Front/ShadowDropper","title":"ShadowDropper","description":"Copying and pasting? We\'ve got you covered! You can find the full source code of this tutorial here.","sidebar":"tutorialSidebar"},"Tutorials/Components/index":{"id":"Tutorials/Components/index","title":"Components","description":"TOC","sidebar":"tutorialSidebar"},"Tutorials/UserInterface/Core/Component":{"id":"Tutorials/UserInterface/Core/Component","title":"Component","description":"Copying and pasting? We\'ve got you covered! You can find the full source code of this tutorial here.","sidebar":"tutorialSidebar"},"Tutorials/UserInterface/index":{"id":"Tutorials/UserInterface/index","title":"UserInterface","description":"TOC","sidebar":"tutorialSidebar"},"Tutorials/UserInterface/OBC/ClassificationsTree":{"id":"Tutorials/UserInterface/OBC/ClassificationsTree","title":"ClassificationsTree","description":"Copying and pasting? We\'ve got you covered! You can find the full source code of this tutorial here.","sidebar":"tutorialSidebar"},"Tutorials/UserInterface/OBC/ElementProperties":{"id":"Tutorials/UserInterface/OBC/ElementProperties","title":"ElementProperties","description":"Copying and pasting? We\'ve got you covered! You can find the full source code of this tutorial here.","sidebar":"tutorialSidebar"},"Tutorials/UserInterface/OBC/EntityAttributes":{"id":"Tutorials/UserInterface/OBC/EntityAttributes","title":"EntityAttributes","description":"Copying and pasting? We\'ve got you covered! You can find the full source code of this tutorial here.","sidebar":"tutorialSidebar"},"Tutorials/UserInterface/OBC/ModelsList":{"id":"Tutorials/UserInterface/OBC/ModelsList","title":"ModelsList","description":"Copying and pasting? We\'ve got you covered! You can find the full source code of this tutorial here.","sidebar":"tutorialSidebar"}}}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkdocs=self.webpackChunkdocs||[]).push([[53],{1109:e=>{e.exports=JSON.parse('{"pluginId":"default","version":"current","label":"Next","banner":null,"badge":false,"noIndex":false,"className":"docs-version-current","isLast":true,"docsSidebars":{"tutorialSidebar":[{"type":"link","label":"\ud83d\udc68\ud83c\udffb\u200d\ud83d\udcbb Introduction","href":"/intro","docId":"intro"},{"type":"link","label":"\ud83e\udd1d Get involved","href":"/contributing","docId":"contributing"},{"type":"category","label":"\ud83e\udde9 Components","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"\ud83d\ude80 Getting started","href":"/components/getting-started","docId":"components/getting-started"},{"type":"link","label":"\ud83e\uddbe Making your own","href":"/components/creating-components","docId":"components/creating-components"},{"type":"link","label":"\ud83e\uddf9 Keeping them clean","href":"/components/clean-components-guide","docId":"components/clean-components-guide"},{"type":"link","label":"\ud83e\udded Tutorial paths","href":"/components/tutorial-paths","docId":"components/tutorial-paths"}]},{"type":"category","label":"API","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"Exports","href":"/api/modules","docId":"api/modules"},{"type":"category","label":"Modules","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"@thatopen/components","href":"/api/modules/thatopen_components","docId":"api/modules/thatopen_components"},{"type":"link","label":"@thatopen/components-front","href":"/api/modules/thatopen_components_front","docId":"api/modules/thatopen_components_front"},{"type":"link","label":"@thatopen/fragments","href":"/api/modules/thatopen_fragments","docId":"api/modules/thatopen_fragments"},{"type":"link","label":"@thatopen/ui","href":"/api/modules/thatopen_ui","docId":"api/modules/thatopen_ui"},{"type":"link","label":"@thatopen/ui-obc","href":"/api/modules/thatopen_ui_obc","docId":"api/modules/thatopen_ui_obc"}]},{"type":"category","label":"Classes","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"AsyncEvent","href":"/api/classes/thatopen_components.AsyncEvent","docId":"api/classes/thatopen_components.AsyncEvent"},{"type":"link","label":"Base","href":"/api/classes/thatopen_components.Base","docId":"api/classes/thatopen_components.Base"},{"type":"link","label":"BaseWorldItem","href":"/api/classes/thatopen_components.BaseWorldItem","docId":"api/classes/thatopen_components.BaseWorldItem"},{"type":"link","label":"BoundingBoxer","href":"/api/classes/thatopen_components.BoundingBoxer","docId":"api/classes/thatopen_components.BoundingBoxer"},{"type":"link","label":"Clipper","href":"/api/classes/thatopen_components.Clipper","docId":"api/classes/thatopen_components.Clipper"},{"type":"link","label":"Component","href":"/api/classes/thatopen_components.Component","docId":"api/classes/thatopen_components.Component"},{"type":"link","label":"Components","href":"/api/classes/thatopen_components.Components","docId":"api/classes/thatopen_components.Components"},{"type":"link","label":"CullerRenderer","href":"/api/classes/thatopen_components.CullerRenderer","docId":"api/classes/thatopen_components.CullerRenderer"},{"type":"link","label":"Cullers","href":"/api/classes/thatopen_components.Cullers","docId":"api/classes/thatopen_components.Cullers"},{"type":"link","label":"Disposer","href":"/api/classes/thatopen_components.Disposer","docId":"api/classes/thatopen_components.Disposer"},{"type":"link","label":"Event","href":"/api/classes/thatopen_components.Event","docId":"api/classes/thatopen_components.Event"},{"type":"link","label":"FirstPersonMode","href":"/api/classes/thatopen_components.FirstPersonMode","docId":"api/classes/thatopen_components.FirstPersonMode"},{"type":"link","label":"FragmentsManager","href":"/api/classes/thatopen_components.FragmentsManager","docId":"api/classes/thatopen_components.FragmentsManager"},{"type":"link","label":"IfcJsonExporter","href":"/api/classes/thatopen_components.IfcJsonExporter","docId":"api/classes/thatopen_components.IfcJsonExporter"},{"type":"link","label":"IfcRelationsIndexer","href":"/api/classes/thatopen_components.IfcRelationsIndexer","docId":"api/classes/thatopen_components.IfcRelationsIndexer"},{"type":"link","label":"IfcStreamingSettings","href":"/api/classes/thatopen_components.IfcStreamingSettings","docId":"api/classes/thatopen_components.IfcStreamingSettings"},{"type":"link","label":"MeshCullerRenderer","href":"/api/classes/thatopen_components.MeshCullerRenderer","docId":"api/classes/thatopen_components.MeshCullerRenderer"},{"type":"link","label":"OrbitMode","href":"/api/classes/thatopen_components.OrbitMode","docId":"api/classes/thatopen_components.OrbitMode"},{"type":"link","label":"OrthoPerspectiveCamera","href":"/api/classes/thatopen_components.OrthoPerspectiveCamera","docId":"api/classes/thatopen_components.OrthoPerspectiveCamera"},{"type":"link","label":"PlanMode","href":"/api/classes/thatopen_components.PlanMode","docId":"api/classes/thatopen_components.PlanMode"},{"type":"link","label":"ProjectionManager","href":"/api/classes/thatopen_components.ProjectionManager","docId":"api/classes/thatopen_components.ProjectionManager"},{"type":"link","label":"PropertiesStreamingSettings","href":"/api/classes/thatopen_components.PropertiesStreamingSettings","docId":"api/classes/thatopen_components.PropertiesStreamingSettings"},{"type":"link","label":"SimpleCamera","href":"/api/classes/thatopen_components.SimpleCamera","docId":"api/classes/thatopen_components.SimpleCamera"},{"type":"link","label":"SimplePlane","href":"/api/classes/thatopen_components.SimplePlane","docId":"api/classes/thatopen_components.SimplePlane"},{"type":"link","label":"SimpleRenderer","href":"/api/classes/thatopen_components.SimpleRenderer","docId":"api/classes/thatopen_components.SimpleRenderer"},{"type":"link","label":"SimpleScene","href":"/api/classes/thatopen_components.SimpleScene","docId":"api/classes/thatopen_components.SimpleScene"},{"type":"link","label":"ClipEdges","href":"/api/classes/thatopen_components_front.ClipEdges","docId":"api/classes/thatopen_components_front.ClipEdges"},{"type":"link","label":"EdgesPlane","href":"/api/classes/thatopen_components_front.EdgesPlane","docId":"api/classes/thatopen_components_front.EdgesPlane"},{"type":"link","label":"LengthMeasurement","href":"/api/classes/thatopen_components_front.LengthMeasurement","docId":"api/classes/thatopen_components_front.LengthMeasurement"},{"type":"link","label":"Marker","href":"/api/classes/thatopen_components_front.Marker","docId":"api/classes/thatopen_components_front.Marker"},{"type":"link","label":"Plans","href":"/api/classes/thatopen_components_front.Plans","docId":"api/classes/thatopen_components_front.Plans"},{"type":"link","label":"PostproductionRenderer","href":"/api/classes/thatopen_components_front.PostproductionRenderer","docId":"api/classes/thatopen_components_front.PostproductionRenderer"},{"type":"link","label":"RendererWith2D","href":"/api/classes/thatopen_components_front.RendererWith2D","docId":"api/classes/thatopen_components_front.RendererWith2D"},{"type":"link","label":"Serializer","href":"/api/classes/thatopen_fragments.Serializer","docId":"api/classes/thatopen_fragments.Serializer"},{"type":"link","label":"Button","href":"/api/classes/thatopen_ui.Button","docId":"api/classes/thatopen_ui.Button"},{"type":"link","label":"Checkbox","href":"/api/classes/thatopen_ui.Checkbox","docId":"api/classes/thatopen_ui.Checkbox"},{"type":"link","label":"ColorInput","href":"/api/classes/thatopen_ui.ColorInput","docId":"api/classes/thatopen_ui.ColorInput"},{"type":"link","label":"Component","href":"/api/classes/thatopen_ui.Component","docId":"api/classes/thatopen_ui.Component"},{"type":"link","label":"ContextMenu","href":"/api/classes/thatopen_ui.ContextMenu","docId":"api/classes/thatopen_ui.ContextMenu"},{"type":"link","label":"Dropdown","href":"/api/classes/thatopen_ui.Dropdown","docId":"api/classes/thatopen_ui.Dropdown"},{"type":"link","label":"Grid","href":"/api/classes/thatopen_ui.Grid","docId":"api/classes/thatopen_ui.Grid"},{"type":"link","label":"Icon","href":"/api/classes/thatopen_ui.Icon","docId":"api/classes/thatopen_ui.Icon"},{"type":"link","label":"Input","href":"/api/classes/thatopen_ui.Input","docId":"api/classes/thatopen_ui.Input"},{"type":"link","label":"Label","href":"/api/classes/thatopen_ui.Label","docId":"api/classes/thatopen_ui.Label"},{"type":"link","label":"NumberInput","href":"/api/classes/thatopen_ui.NumberInput","docId":"api/classes/thatopen_ui.NumberInput"},{"type":"link","label":"Option","href":"/api/classes/thatopen_ui.Option","docId":"api/classes/thatopen_ui.Option"},{"type":"link","label":"Panel","href":"/api/classes/thatopen_ui.Panel","docId":"api/classes/thatopen_ui.Panel"},{"type":"link","label":"PanelSection","href":"/api/classes/thatopen_ui.PanelSection","docId":"api/classes/thatopen_ui.PanelSection"},{"type":"link","label":"Selector","href":"/api/classes/thatopen_ui.Selector","docId":"api/classes/thatopen_ui.Selector"},{"type":"link","label":"Tab","href":"/api/classes/thatopen_ui.Tab","docId":"api/classes/thatopen_ui.Tab"},{"type":"link","label":"Table","href":"/api/classes/thatopen_ui.Table","docId":"api/classes/thatopen_ui.Table"},{"type":"link","label":"Tabs","href":"/api/classes/thatopen_ui.Tabs","docId":"api/classes/thatopen_ui.Tabs"},{"type":"link","label":"TextInput","href":"/api/classes/thatopen_ui.TextInput","docId":"api/classes/thatopen_ui.TextInput"},{"type":"link","label":"Toolbar","href":"/api/classes/thatopen_ui.Toolbar","docId":"api/classes/thatopen_ui.Toolbar"},{"type":"link","label":"ToolbarGroup","href":"/api/classes/thatopen_ui.ToolbarGroup","docId":"api/classes/thatopen_ui.ToolbarGroup"},{"type":"link","label":"ToolbarSection","href":"/api/classes/thatopen_ui.ToolbarSection","docId":"api/classes/thatopen_ui.ToolbarSection"},{"type":"link","label":"Viewport","href":"/api/classes/thatopen_ui.Viewport","docId":"api/classes/thatopen_ui.Viewport"},{"type":"link","label":"Manager","href":"/api/classes/thatopen_ui_obc.Manager","docId":"api/classes/thatopen_ui_obc.Manager"},{"type":"link","label":"ViewCube","href":"/api/classes/thatopen_ui_obc.ViewCube","docId":"api/classes/thatopen_ui_obc.ViewCube"},{"type":"link","label":"World2D","href":"/api/classes/thatopen_ui_obc.World2D","docId":"api/classes/thatopen_ui_obc.World2D"}]},{"type":"category","label":"Interfaces","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"BVHGeometry","href":"/api/interfaces/thatopen_components.BVHGeometry","docId":"api/interfaces/thatopen_components.BVHGeometry"},{"type":"link","label":"CameraControllable","href":"/api/interfaces/thatopen_components.CameraControllable","docId":"api/interfaces/thatopen_components.CameraControllable"},{"type":"link","label":"Configurable","href":"/api/interfaces/thatopen_components.Configurable","docId":"api/interfaces/thatopen_components.Configurable"},{"type":"link","label":"Createable","href":"/api/interfaces/thatopen_components.Createable","docId":"api/interfaces/thatopen_components.Createable"},{"type":"link","label":"Disposable","href":"/api/interfaces/thatopen_components.Disposable","docId":"api/interfaces/thatopen_components.Disposable"},{"type":"link","label":"Hideable","href":"/api/interfaces/thatopen_components.Hideable","docId":"api/interfaces/thatopen_components.Hideable"},{"type":"link","label":"NavigationMode","href":"/api/interfaces/thatopen_components.NavigationMode","docId":"api/interfaces/thatopen_components.NavigationMode"},{"type":"link","label":"Progress","href":"/api/interfaces/thatopen_components.Progress","docId":"api/interfaces/thatopen_components.Progress"},{"type":"link","label":"Resizeable","href":"/api/interfaces/thatopen_components.Resizeable","docId":"api/interfaces/thatopen_components.Resizeable"},{"type":"link","label":"Updateable","href":"/api/interfaces/thatopen_components.Updateable","docId":"api/interfaces/thatopen_components.Updateable"},{"type":"link","label":"ColumnData","href":"/api/interfaces/thatopen_ui.ColumnData","docId":"api/interfaces/thatopen_ui.ColumnData"},{"type":"link","label":"EntryQuery","href":"/api/interfaces/thatopen_ui.EntryQuery","docId":"api/interfaces/thatopen_ui.EntryQuery"},{"type":"link","label":"HasName","href":"/api/interfaces/thatopen_ui.HasName","docId":"api/interfaces/thatopen_ui.HasName"},{"type":"link","label":"HasValue","href":"/api/interfaces/thatopen_ui.HasValue","docId":"api/interfaces/thatopen_ui.HasValue"},{"type":"link","label":"QueryGroup","href":"/api/interfaces/thatopen_ui.QueryGroup","docId":"api/interfaces/thatopen_ui.QueryGroup"}]}],"href":"/api/"},{"type":"category","label":"Tutorials","collapsible":true,"collapsed":true,"items":[{"type":"category","label":"Components","collapsible":true,"collapsed":true,"items":[{"type":"category","label":"Core","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"BoundingBoxer","href":"/Tutorials/Components/Core/BoundingBoxer","docId":"Tutorials/Components/Core/BoundingBoxer"},{"type":"link","label":"Classifier","href":"/Tutorials/Components/Core/Classifier","docId":"Tutorials/Components/Core/Classifier"},{"type":"link","label":"Clipper","href":"/Tutorials/Components/Core/Clipper","docId":"Tutorials/Components/Core/Clipper"},{"type":"link","label":"Cullers","href":"/Tutorials/Components/Core/Cullers","docId":"Tutorials/Components/Core/Cullers"},{"type":"link","label":"Exploder","href":"/Tutorials/Components/Core/Exploder","docId":"Tutorials/Components/Core/Exploder"},{"type":"link","label":"FragmentsManager","href":"/Tutorials/Components/Core/FragmentsManager","docId":"Tutorials/Components/Core/FragmentsManager"},{"type":"link","label":"Grids","href":"/Tutorials/Components/Core/Grids","docId":"Tutorials/Components/Core/Grids"},{"type":"link","label":"Hider","href":"/Tutorials/Components/Core/Hider","docId":"Tutorials/Components/Core/Hider"},{"type":"link","label":"IfcGeometryTiler","href":"/Tutorials/Components/Core/IfcGeometryTiler","docId":"Tutorials/Components/Core/IfcGeometryTiler"},{"type":"link","label":"IfcLoader","href":"/Tutorials/Components/Core/IfcLoader","docId":"Tutorials/Components/Core/IfcLoader"},{"type":"link","label":"IfcPropertiesTiler","href":"/Tutorials/Components/Core/IfcPropertiesTiler","docId":"Tutorials/Components/Core/IfcPropertiesTiler"},{"type":"link","label":"IfcRelationsIndexer","href":"/Tutorials/Components/Core/IfcRelationsIndexer","docId":"Tutorials/Components/Core/IfcRelationsIndexer"},{"type":"link","label":"MiniMap","href":"/Tutorials/Components/Core/MiniMap","docId":"Tutorials/Components/Core/MiniMap"},{"type":"link","label":"OrthoPerspectiveCamera","href":"/Tutorials/Components/Core/OrthoPerspectiveCamera","docId":"Tutorials/Components/Core/OrthoPerspectiveCamera"},{"type":"link","label":"Raycasters","href":"/Tutorials/Components/Core/Raycasters","docId":"Tutorials/Components/Core/Raycasters"},{"type":"link","label":"Worlds","href":"/Tutorials/Components/Core/Worlds","docId":"Tutorials/Components/Core/Worlds"}]},{"type":"category","label":"Front","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"AngleMeasurement","href":"/Tutorials/Components/Front/AngleMeasurement","docId":"Tutorials/Components/Front/AngleMeasurement"},{"type":"link","label":"AreaMeasurement","href":"/Tutorials/Components/Front/AreaMeasurement","docId":"Tutorials/Components/Front/AreaMeasurement"},{"type":"link","label":"EdgesClipper","href":"/Tutorials/Components/Front/EdgesClipper","docId":"Tutorials/Components/Front/EdgesClipper"},{"type":"link","label":"IfcStreamer","href":"/Tutorials/Components/Front/IfcStreamer","docId":"Tutorials/Components/Front/IfcStreamer"},{"type":"link","label":"LengthMeasurement","href":"/Tutorials/Components/Front/LengthMeasurement","docId":"Tutorials/Components/Front/LengthMeasurement"},{"type":"link","label":"PostproductionRenderer","href":"/Tutorials/Components/Front/PostproductionRenderer","docId":"Tutorials/Components/Front/PostproductionRenderer"},{"type":"link","label":"ShadowDropper","href":"/Tutorials/Components/Front/ShadowDropper","docId":"Tutorials/Components/Front/ShadowDropper"}]}],"href":"/Tutorials/Components/"},{"type":"category","label":"UserInterface","collapsible":true,"collapsed":true,"items":[{"type":"category","label":"Core","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"Component","href":"/Tutorials/UserInterface/Core/Component","docId":"Tutorials/UserInterface/Core/Component"}]},{"type":"category","label":"OBC","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"ClassificationsTree","href":"/Tutorials/UserInterface/OBC/ClassificationsTree","docId":"Tutorials/UserInterface/OBC/ClassificationsTree"},{"type":"link","label":"ElementProperties","href":"/Tutorials/UserInterface/OBC/ElementProperties","docId":"Tutorials/UserInterface/OBC/ElementProperties"},{"type":"link","label":"EntityAttributes","href":"/Tutorials/UserInterface/OBC/EntityAttributes","docId":"Tutorials/UserInterface/OBC/EntityAttributes"},{"type":"link","label":"ModelsList","href":"/Tutorials/UserInterface/OBC/ModelsList","docId":"Tutorials/UserInterface/OBC/ModelsList"}]}],"href":"/Tutorials/UserInterface/"}]}]},"docs":{"api/classes/thatopen_components_front.ClipEdges":{"id":"api/classes/thatopen_components_front.ClipEdges","title":"Class: ClipEdges","description":"@thatopen/components-front.ClipEdges","sidebar":"tutorialSidebar"},"api/classes/thatopen_components_front.EdgesPlane":{"id":"api/classes/thatopen_components_front.EdgesPlane","title":"Class: EdgesPlane","description":"@thatopen/components-front.EdgesPlane","sidebar":"tutorialSidebar"},"api/classes/thatopen_components_front.LengthMeasurement":{"id":"api/classes/thatopen_components_front.LengthMeasurement","title":"Class: LengthMeasurement","description":"@thatopen/components-front.LengthMeasurement","sidebar":"tutorialSidebar"},"api/classes/thatopen_components_front.Marker":{"id":"api/classes/thatopen_components_front.Marker","title":"Class: Marker","description":"@thatopen/components-front.Marker","sidebar":"tutorialSidebar"},"api/classes/thatopen_components_front.Plans":{"id":"api/classes/thatopen_components_front.Plans","title":"Class: Plans","description":"@thatopen/components-front.Plans","sidebar":"tutorialSidebar"},"api/classes/thatopen_components_front.PostproductionRenderer":{"id":"api/classes/thatopen_components_front.PostproductionRenderer","title":"Class: PostproductionRenderer","description":"@thatopen/components-front.PostproductionRenderer","sidebar":"tutorialSidebar"},"api/classes/thatopen_components_front.RendererWith2D":{"id":"api/classes/thatopen_components_front.RendererWith2D","title":"Class: RendererWith2D","description":"@thatopen/components-front.RendererWith2D","sidebar":"tutorialSidebar"},"api/classes/thatopen_components.AsyncEvent":{"id":"api/classes/thatopen_components.AsyncEvent","title":"Class: AsyncEvent","description":"@thatopen/components.AsyncEvent","sidebar":"tutorialSidebar"},"api/classes/thatopen_components.Base":{"id":"api/classes/thatopen_components.Base","title":"Class: Base","description":"@thatopen/components.Base","sidebar":"tutorialSidebar"},"api/classes/thatopen_components.BaseWorldItem":{"id":"api/classes/thatopen_components.BaseWorldItem","title":"Class: BaseWorldItem","description":"@thatopen/components.BaseWorldItem","sidebar":"tutorialSidebar"},"api/classes/thatopen_components.BoundingBoxer":{"id":"api/classes/thatopen_components.BoundingBoxer","title":"Class: BoundingBoxer","description":"@thatopen/components.BoundingBoxer","sidebar":"tutorialSidebar"},"api/classes/thatopen_components.Clipper":{"id":"api/classes/thatopen_components.Clipper","title":"Class: Clipper","description":"@thatopen/components.Clipper","sidebar":"tutorialSidebar"},"api/classes/thatopen_components.Component":{"id":"api/classes/thatopen_components.Component","title":"Class: Component","description":"@thatopen/components.Component","sidebar":"tutorialSidebar"},"api/classes/thatopen_components.Components":{"id":"api/classes/thatopen_components.Components","title":"Class: Components","description":"@thatopen/components.Components","sidebar":"tutorialSidebar"},"api/classes/thatopen_components.CullerRenderer":{"id":"api/classes/thatopen_components.CullerRenderer","title":"Class: CullerRenderer","description":"@thatopen/components.CullerRenderer","sidebar":"tutorialSidebar"},"api/classes/thatopen_components.Cullers":{"id":"api/classes/thatopen_components.Cullers","title":"Class: Cullers","description":"@thatopen/components.Cullers","sidebar":"tutorialSidebar"},"api/classes/thatopen_components.Disposer":{"id":"api/classes/thatopen_components.Disposer","title":"Class: Disposer","description":"@thatopen/components.Disposer","sidebar":"tutorialSidebar"},"api/classes/thatopen_components.Event":{"id":"api/classes/thatopen_components.Event","title":"Class: Event","description":"@thatopen/components.Event","sidebar":"tutorialSidebar"},"api/classes/thatopen_components.FirstPersonMode":{"id":"api/classes/thatopen_components.FirstPersonMode","title":"Class: FirstPersonMode","description":"@thatopen/components.FirstPersonMode","sidebar":"tutorialSidebar"},"api/classes/thatopen_components.FragmentsManager":{"id":"api/classes/thatopen_components.FragmentsManager","title":"Class: FragmentsManager","description":"@thatopen/components.FragmentsManager","sidebar":"tutorialSidebar"},"api/classes/thatopen_components.IfcJsonExporter":{"id":"api/classes/thatopen_components.IfcJsonExporter","title":"Class: IfcJsonExporter","description":"@thatopen/components.IfcJsonExporter","sidebar":"tutorialSidebar"},"api/classes/thatopen_components.IfcRelationsIndexer":{"id":"api/classes/thatopen_components.IfcRelationsIndexer","title":"Class: IfcRelationsIndexer","description":"@thatopen/components.IfcRelationsIndexer","sidebar":"tutorialSidebar"},"api/classes/thatopen_components.IfcStreamingSettings":{"id":"api/classes/thatopen_components.IfcStreamingSettings","title":"Class: IfcStreamingSettings","description":"@thatopen/components.IfcStreamingSettings","sidebar":"tutorialSidebar"},"api/classes/thatopen_components.MeshCullerRenderer":{"id":"api/classes/thatopen_components.MeshCullerRenderer","title":"Class: MeshCullerRenderer","description":"@thatopen/components.MeshCullerRenderer","sidebar":"tutorialSidebar"},"api/classes/thatopen_components.OrbitMode":{"id":"api/classes/thatopen_components.OrbitMode","title":"Class: OrbitMode","description":"@thatopen/components.OrbitMode","sidebar":"tutorialSidebar"},"api/classes/thatopen_components.OrthoPerspectiveCamera":{"id":"api/classes/thatopen_components.OrthoPerspectiveCamera","title":"Class: OrthoPerspectiveCamera","description":"@thatopen/components.OrthoPerspectiveCamera","sidebar":"tutorialSidebar"},"api/classes/thatopen_components.PlanMode":{"id":"api/classes/thatopen_components.PlanMode","title":"Class: PlanMode","description":"@thatopen/components.PlanMode","sidebar":"tutorialSidebar"},"api/classes/thatopen_components.ProjectionManager":{"id":"api/classes/thatopen_components.ProjectionManager","title":"Class: ProjectionManager","description":"@thatopen/components.ProjectionManager","sidebar":"tutorialSidebar"},"api/classes/thatopen_components.PropertiesStreamingSettings":{"id":"api/classes/thatopen_components.PropertiesStreamingSettings","title":"Class: PropertiesStreamingSettings","description":"@thatopen/components.PropertiesStreamingSettings","sidebar":"tutorialSidebar"},"api/classes/thatopen_components.SimpleCamera":{"id":"api/classes/thatopen_components.SimpleCamera","title":"Class: SimpleCamera","description":"@thatopen/components.SimpleCamera","sidebar":"tutorialSidebar"},"api/classes/thatopen_components.SimplePlane":{"id":"api/classes/thatopen_components.SimplePlane","title":"Class: SimplePlane","description":"@thatopen/components.SimplePlane","sidebar":"tutorialSidebar"},"api/classes/thatopen_components.SimpleRenderer":{"id":"api/classes/thatopen_components.SimpleRenderer","title":"Class: SimpleRenderer","description":"@thatopen/components.SimpleRenderer","sidebar":"tutorialSidebar"},"api/classes/thatopen_components.SimpleScene":{"id":"api/classes/thatopen_components.SimpleScene","title":"Class: SimpleScene","description":"@thatopen/components.SimpleScene","sidebar":"tutorialSidebar"},"api/classes/thatopen_fragments.Serializer":{"id":"api/classes/thatopen_fragments.Serializer","title":"Class: Serializer","description":"@thatopen/fragments.Serializer","sidebar":"tutorialSidebar"},"api/classes/thatopen_ui_obc.Manager":{"id":"api/classes/thatopen_ui_obc.Manager","title":"Class: Manager","description":"@thatopen/ui-obc.Manager","sidebar":"tutorialSidebar"},"api/classes/thatopen_ui_obc.ViewCube":{"id":"api/classes/thatopen_ui_obc.ViewCube","title":"Class: ViewCube","description":"@thatopen/ui-obc.ViewCube","sidebar":"tutorialSidebar"},"api/classes/thatopen_ui_obc.World2D":{"id":"api/classes/thatopen_ui_obc.World2D","title":"Class: World2D","description":"@thatopen/ui-obc.World2D","sidebar":"tutorialSidebar"},"api/classes/thatopen_ui.Button":{"id":"api/classes/thatopen_ui.Button","title":"Class: Button","description":"@thatopen/ui.Button","sidebar":"tutorialSidebar"},"api/classes/thatopen_ui.Checkbox":{"id":"api/classes/thatopen_ui.Checkbox","title":"Class: Checkbox","description":"@thatopen/ui.Checkbox","sidebar":"tutorialSidebar"},"api/classes/thatopen_ui.ColorInput":{"id":"api/classes/thatopen_ui.ColorInput","title":"Class: ColorInput","description":"@thatopen/ui.ColorInput","sidebar":"tutorialSidebar"},"api/classes/thatopen_ui.Component":{"id":"api/classes/thatopen_ui.Component","title":"Class: Component","description":"@thatopen/ui.Component","sidebar":"tutorialSidebar"},"api/classes/thatopen_ui.ContextMenu":{"id":"api/classes/thatopen_ui.ContextMenu","title":"Class: ContextMenu","description":"@thatopen/ui.ContextMenu","sidebar":"tutorialSidebar"},"api/classes/thatopen_ui.Dropdown":{"id":"api/classes/thatopen_ui.Dropdown","title":"Class: Dropdown","description":"@thatopen/ui.Dropdown","sidebar":"tutorialSidebar"},"api/classes/thatopen_ui.Grid":{"id":"api/classes/thatopen_ui.Grid","title":"Class: Grid","description":"@thatopen/ui.Grid","sidebar":"tutorialSidebar"},"api/classes/thatopen_ui.Icon":{"id":"api/classes/thatopen_ui.Icon","title":"Class: Icon","description":"@thatopen/ui.Icon","sidebar":"tutorialSidebar"},"api/classes/thatopen_ui.Input":{"id":"api/classes/thatopen_ui.Input","title":"Class: Input","description":"@thatopen/ui.Input","sidebar":"tutorialSidebar"},"api/classes/thatopen_ui.Label":{"id":"api/classes/thatopen_ui.Label","title":"Class: Label","description":"@thatopen/ui.Label","sidebar":"tutorialSidebar"},"api/classes/thatopen_ui.NumberInput":{"id":"api/classes/thatopen_ui.NumberInput","title":"Class: NumberInput","description":"@thatopen/ui.NumberInput","sidebar":"tutorialSidebar"},"api/classes/thatopen_ui.Option":{"id":"api/classes/thatopen_ui.Option","title":"Class: Option","description":"@thatopen/ui.Option","sidebar":"tutorialSidebar"},"api/classes/thatopen_ui.Panel":{"id":"api/classes/thatopen_ui.Panel","title":"Class: Panel","description":"@thatopen/ui.Panel","sidebar":"tutorialSidebar"},"api/classes/thatopen_ui.PanelSection":{"id":"api/classes/thatopen_ui.PanelSection","title":"Class: PanelSection","description":"@thatopen/ui.PanelSection","sidebar":"tutorialSidebar"},"api/classes/thatopen_ui.Selector":{"id":"api/classes/thatopen_ui.Selector","title":"Class: Selector","description":"@thatopen/ui.Selector","sidebar":"tutorialSidebar"},"api/classes/thatopen_ui.Tab":{"id":"api/classes/thatopen_ui.Tab","title":"Class: Tab","description":"@thatopen/ui.Tab","sidebar":"tutorialSidebar"},"api/classes/thatopen_ui.Table":{"id":"api/classes/thatopen_ui.Table","title":"Class: Table","description":"@thatopen/ui.Table","sidebar":"tutorialSidebar"},"api/classes/thatopen_ui.Tabs":{"id":"api/classes/thatopen_ui.Tabs","title":"Class: Tabs","description":"@thatopen/ui.Tabs","sidebar":"tutorialSidebar"},"api/classes/thatopen_ui.TextInput":{"id":"api/classes/thatopen_ui.TextInput","title":"Class: TextInput","description":"@thatopen/ui.TextInput","sidebar":"tutorialSidebar"},"api/classes/thatopen_ui.Toolbar":{"id":"api/classes/thatopen_ui.Toolbar","title":"Class: Toolbar","description":"@thatopen/ui.Toolbar","sidebar":"tutorialSidebar"},"api/classes/thatopen_ui.ToolbarGroup":{"id":"api/classes/thatopen_ui.ToolbarGroup","title":"Class: ToolbarGroup","description":"@thatopen/ui.ToolbarGroup","sidebar":"tutorialSidebar"},"api/classes/thatopen_ui.ToolbarSection":{"id":"api/classes/thatopen_ui.ToolbarSection","title":"Class: ToolbarSection","description":"@thatopen/ui.ToolbarSection","sidebar":"tutorialSidebar"},"api/classes/thatopen_ui.Viewport":{"id":"api/classes/thatopen_ui.Viewport","title":"Class: Viewport","description":"@thatopen/ui.Viewport","sidebar":"tutorialSidebar"},"api/index":{"id":"api/index","title":"Open BIM Docs","description":"TOC","sidebar":"tutorialSidebar"},"api/interfaces/thatopen_components.BVHGeometry":{"id":"api/interfaces/thatopen_components.BVHGeometry","title":"Interface: BVHGeometry","description":"@thatopen/components.BVHGeometry","sidebar":"tutorialSidebar"},"api/interfaces/thatopen_components.CameraControllable":{"id":"api/interfaces/thatopen_components.CameraControllable","title":"Interface: CameraControllable","description":"@thatopen/components.CameraControllable","sidebar":"tutorialSidebar"},"api/interfaces/thatopen_components.Configurable":{"id":"api/interfaces/thatopen_components.Configurable","title":"Interface: Configurable","description":"@thatopen/components.Configurable","sidebar":"tutorialSidebar"},"api/interfaces/thatopen_components.Createable":{"id":"api/interfaces/thatopen_components.Createable","title":"Interface: Createable","description":"@thatopen/components.Createable","sidebar":"tutorialSidebar"},"api/interfaces/thatopen_components.Disposable":{"id":"api/interfaces/thatopen_components.Disposable","title":"Interface: Disposable","description":"@thatopen/components.Disposable","sidebar":"tutorialSidebar"},"api/interfaces/thatopen_components.Hideable":{"id":"api/interfaces/thatopen_components.Hideable","title":"Interface: Hideable","description":"@thatopen/components.Hideable","sidebar":"tutorialSidebar"},"api/interfaces/thatopen_components.NavigationMode":{"id":"api/interfaces/thatopen_components.NavigationMode","title":"Interface: NavigationMode","description":"@thatopen/components.NavigationMode","sidebar":"tutorialSidebar"},"api/interfaces/thatopen_components.Progress":{"id":"api/interfaces/thatopen_components.Progress","title":"Interface: Progress","description":"@thatopen/components.Progress","sidebar":"tutorialSidebar"},"api/interfaces/thatopen_components.Resizeable":{"id":"api/interfaces/thatopen_components.Resizeable","title":"Interface: Resizeable","description":"@thatopen/components.Resizeable","sidebar":"tutorialSidebar"},"api/interfaces/thatopen_components.Updateable":{"id":"api/interfaces/thatopen_components.Updateable","title":"Interface: Updateable","description":"@thatopen/components.Updateable","sidebar":"tutorialSidebar"},"api/interfaces/thatopen_ui.ColumnData":{"id":"api/interfaces/thatopen_ui.ColumnData","title":"Interface: ColumnData","description":"@thatopen/ui.ColumnData","sidebar":"tutorialSidebar"},"api/interfaces/thatopen_ui.EntryQuery":{"id":"api/interfaces/thatopen_ui.EntryQuery","title":"Interface: EntryQuery","description":"@thatopen/ui.EntryQuery","sidebar":"tutorialSidebar"},"api/interfaces/thatopen_ui.HasName":{"id":"api/interfaces/thatopen_ui.HasName","title":"Interface: HasName","description":"@thatopen/ui.HasName","sidebar":"tutorialSidebar"},"api/interfaces/thatopen_ui.HasValue":{"id":"api/interfaces/thatopen_ui.HasValue","title":"Interface: HasValue","description":"@thatopen/ui.HasValue","sidebar":"tutorialSidebar"},"api/interfaces/thatopen_ui.QueryGroup":{"id":"api/interfaces/thatopen_ui.QueryGroup","title":"Interface: QueryGroup","description":"@thatopen/ui.QueryGroup","sidebar":"tutorialSidebar"},"api/modules":{"id":"api/modules","title":"Open BIM Docs","description":"","sidebar":"tutorialSidebar"},"api/modules/thatopen_components":{"id":"api/modules/thatopen_components","title":"Module: @thatopen/components","description":"Classes","sidebar":"tutorialSidebar"},"api/modules/thatopen_components_front":{"id":"api/modules/thatopen_components_front","title":"Module: @thatopen/components-front","description":"Classes","sidebar":"tutorialSidebar"},"api/modules/thatopen_fragments":{"id":"api/modules/thatopen_fragments","title":"Module: @thatopen/fragments","description":"Classes","sidebar":"tutorialSidebar"},"api/modules/thatopen_ui":{"id":"api/modules/thatopen_ui","title":"Module: @thatopen/ui","description":"Classes","sidebar":"tutorialSidebar"},"api/modules/thatopen_ui_obc":{"id":"api/modules/thatopen_ui_obc","title":"Module: @thatopen/ui-obc","description":"Classes","sidebar":"tutorialSidebar"},"components/clean-components-guide":{"id":"components/clean-components-guide","title":"\ud83e\uddf9 Keeping them clean","description":"\ud83e\uddfd Basics","sidebar":"tutorialSidebar"},"components/creating-components":{"id":"components/creating-components","title":"\ud83e\uddbe Making your own","description":"\ud83d\udc6a Create it","sidebar":"tutorialSidebar"},"components/getting-started":{"id":"components/getting-started","title":"\ud83d\ude80 Getting started","description":"\ud83d\udc69\ud83c\udffb\u200d\ud83c\udfeb Component ABC","sidebar":"tutorialSidebar"},"components/tutorial-paths":{"id":"components/tutorial-paths","title":"\ud83e\udded Tutorial paths","description":"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! \ud83d\udd25\ud83d\udd25\ud83d\ude80","sidebar":"tutorialSidebar"},"contributing":{"id":"contributing","title":"\ud83e\udd1d Get involved","description":"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. \ud83d\udc47\ud83c\udffb","sidebar":"tutorialSidebar"},"intro":{"id":"intro","title":"\ud83d\udc68\ud83c\udffb\u200d\ud83d\udcbb Introduction","description":"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! \ud83c\udfe2\ud83d\udc69\u200d\ud83d\udcbb","sidebar":"tutorialSidebar"},"Tutorials/Components/Core/BoundingBoxer":{"id":"Tutorials/Components/Core/BoundingBoxer","title":"BoundingBoxer","description":"Copying and pasting? We\'ve got you covered! You can find the full source code of this tutorial here.","sidebar":"tutorialSidebar"},"Tutorials/Components/Core/Classifier":{"id":"Tutorials/Components/Core/Classifier","title":"Classifier","description":"Copying and pasting? We\'ve got you covered! You can find the full source code of this tutorial here.","sidebar":"tutorialSidebar"},"Tutorials/Components/Core/Clipper":{"id":"Tutorials/Components/Core/Clipper","title":"Clipper","description":"Copying and pasting? We\'ve got you covered! You can find the full source code of this tutorial here.","sidebar":"tutorialSidebar"},"Tutorials/Components/Core/Cullers":{"id":"Tutorials/Components/Core/Cullers","title":"Cullers","description":"Copying and pasting? We\'ve got you covered! You can find the full source code of this tutorial here.","sidebar":"tutorialSidebar"},"Tutorials/Components/Core/Exploder":{"id":"Tutorials/Components/Core/Exploder","title":"Exploder","description":"Copying and pasting? We\'ve got you covered! You can find the full source code of this tutorial here.","sidebar":"tutorialSidebar"},"Tutorials/Components/Core/FragmentsManager":{"id":"Tutorials/Components/Core/FragmentsManager","title":"FragmentsManager","description":"Copying and pasting? We\'ve got you covered! You can find the full source code of this tutorial here.","sidebar":"tutorialSidebar"},"Tutorials/Components/Core/Grids":{"id":"Tutorials/Components/Core/Grids","title":"Grids","description":"Copying and pasting? We\'ve got you covered! You can find the full source code of this tutorial here.","sidebar":"tutorialSidebar"},"Tutorials/Components/Core/Hider":{"id":"Tutorials/Components/Core/Hider","title":"Hider","description":"Copying and pasting? We\'ve got you covered! You can find the full source code of this tutorial here.","sidebar":"tutorialSidebar"},"Tutorials/Components/Core/IfcGeometryTiler":{"id":"Tutorials/Components/Core/IfcGeometryTiler","title":"IfcGeometryTiler","description":"Copying and pasting? We\'ve got you covered! You can find the full source code of this tutorial here.","sidebar":"tutorialSidebar"},"Tutorials/Components/Core/IfcLoader":{"id":"Tutorials/Components/Core/IfcLoader","title":"IfcLoader","description":"Copying and pasting? We\'ve got you covered! You can find the full source code of this tutorial here.","sidebar":"tutorialSidebar"},"Tutorials/Components/Core/IfcPropertiesTiler":{"id":"Tutorials/Components/Core/IfcPropertiesTiler","title":"IfcPropertiesTiler","description":"Copying and pasting? We\'ve got you covered! You can find the full source code of this tutorial here.","sidebar":"tutorialSidebar"},"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.","sidebar":"tutorialSidebar"},"Tutorials/Components/Core/MiniMap":{"id":"Tutorials/Components/Core/MiniMap","title":"MiniMap","description":"Copying and pasting? We\'ve got you covered! You can find the full source code of this tutorial here.","sidebar":"tutorialSidebar"},"Tutorials/Components/Core/OrthoPerspectiveCamera":{"id":"Tutorials/Components/Core/OrthoPerspectiveCamera","title":"OrthoPerspectiveCamera","description":"Copying and pasting? We\'ve got you covered! You can find the full source code of this tutorial here.","sidebar":"tutorialSidebar"},"Tutorials/Components/Core/Raycasters":{"id":"Tutorials/Components/Core/Raycasters","title":"Raycasters","description":"Copying and pasting? We\'ve got you covered! You can find the full source code of this tutorial here.","sidebar":"tutorialSidebar"},"Tutorials/Components/Core/Worlds":{"id":"Tutorials/Components/Core/Worlds","title":"Worlds","description":"Copying and pasting? We\'ve got you covered! You can find the full source code of this tutorial here.","sidebar":"tutorialSidebar"},"Tutorials/Components/Front/AngleMeasurement":{"id":"Tutorials/Components/Front/AngleMeasurement","title":"AngleMeasurement","description":"Copying and pasting? We\'ve got you covered! You can find the full source code of this tutorial here.","sidebar":"tutorialSidebar"},"Tutorials/Components/Front/AreaMeasurement":{"id":"Tutorials/Components/Front/AreaMeasurement","title":"AreaMeasurement","description":"Copying and pasting? We\'ve got you covered! You can find the full source code of this tutorial here.","sidebar":"tutorialSidebar"},"Tutorials/Components/Front/EdgesClipper":{"id":"Tutorials/Components/Front/EdgesClipper","title":"EdgesClipper","description":"Copying and pasting? We\'ve got you covered! You can find the full source code of this tutorial here.","sidebar":"tutorialSidebar"},"Tutorials/Components/Front/IfcStreamer":{"id":"Tutorials/Components/Front/IfcStreamer","title":"IfcStreamer","description":"Copying and pasting? We\'ve got you covered! You can find the full source code of this tutorial here.","sidebar":"tutorialSidebar"},"Tutorials/Components/Front/LengthMeasurement":{"id":"Tutorials/Components/Front/LengthMeasurement","title":"LengthMeasurement","description":"Copying and pasting? We\'ve got you covered! You can find the full source code of this tutorial here.","sidebar":"tutorialSidebar"},"Tutorials/Components/Front/PostproductionRenderer":{"id":"Tutorials/Components/Front/PostproductionRenderer","title":"PostproductionRenderer","description":"Copying and pasting? We\'ve got you covered! You can find the full source code of this tutorial here.","sidebar":"tutorialSidebar"},"Tutorials/Components/Front/ShadowDropper":{"id":"Tutorials/Components/Front/ShadowDropper","title":"ShadowDropper","description":"Copying and pasting? We\'ve got you covered! You can find the full source code of this tutorial here.","sidebar":"tutorialSidebar"},"Tutorials/Components/index":{"id":"Tutorials/Components/index","title":"Components","description":"TOC","sidebar":"tutorialSidebar"},"Tutorials/UserInterface/Core/Component":{"id":"Tutorials/UserInterface/Core/Component","title":"Component","description":"Copying and pasting? We\'ve got you covered! You can find the full source code of this tutorial here.","sidebar":"tutorialSidebar"},"Tutorials/UserInterface/index":{"id":"Tutorials/UserInterface/index","title":"UserInterface","description":"TOC","sidebar":"tutorialSidebar"},"Tutorials/UserInterface/OBC/ClassificationsTree":{"id":"Tutorials/UserInterface/OBC/ClassificationsTree","title":"ClassificationsTree","description":"Copying and pasting? We\'ve got you covered! You can find the full source code of this tutorial here.","sidebar":"tutorialSidebar"},"Tutorials/UserInterface/OBC/ElementProperties":{"id":"Tutorials/UserInterface/OBC/ElementProperties","title":"ElementProperties","description":"Copying and pasting? We\'ve got you covered! You can find the full source code of this tutorial here.","sidebar":"tutorialSidebar"},"Tutorials/UserInterface/OBC/EntityAttributes":{"id":"Tutorials/UserInterface/OBC/EntityAttributes","title":"EntityAttributes","description":"Copying and pasting? We\'ve got you covered! You can find the full source code of this tutorial here.","sidebar":"tutorialSidebar"},"Tutorials/UserInterface/OBC/ModelsList":{"id":"Tutorials/UserInterface/OBC/ModelsList","title":"ModelsList","description":"Copying and pasting? We\'ve got you covered! You can find the full source code of this tutorial here.","sidebar":"tutorialSidebar"}}}')}}]); \ No newline at end of file diff --git a/build/assets/js/9f1bf482.0baa44d0.js b/build/assets/js/9f1bf482.4382bbb0.js similarity index 59% rename from build/assets/js/9f1bf482.0baa44d0.js rename to build/assets/js/9f1bf482.4382bbb0.js index d121fcf8c..2bcf06f5c 100644 --- a/build/assets/js/9f1bf482.0baa44d0.js +++ b/build/assets/js/9f1bf482.4382bbb0.js @@ -1 +1 @@ -"use strict";(self.webpackChunkdocs=self.webpackChunkdocs||[]).push([[9311],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>h});var a=n(7294);function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function r(e){for(var t=1;t=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var l=a.createContext({}),c=function(e){var t=a.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):r(r({},t),e)),n},p=function(e){var t=c(e.components);return a.createElement(l.Provider,{value:t},e.children)},u="mdxType",m={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},d=a.forwardRef((function(e,t){var n=e.components,o=e.mdxType,i=e.originalType,l=e.parentName,p=s(e,["components","mdxType","originalType","parentName"]),u=c(n),d=o,h=u["".concat(l,".").concat(d)]||u[d]||m[d]||i;return n?a.createElement(h,r(r({ref:t},p),{},{components:n})):a.createElement(h,r({ref:t},p))}));function h(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var i=n.length,r=new Array(i);r[0]=d;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[u]="string"==typeof e?e:o,r[1]=s;for(var c=2;c{n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>l,default:()=>h,frontMatter:()=>s,metadata:()=>c,toc:()=>u});var a=n(7462),o=n(3366),i=(n(7294),n(3905)),r=["components"],s={},l=void 0,c={unversionedId:"Tutorials/UserInterface/Core/Component",id:"Tutorials/UserInterface/Core/Component",title:"Component",description:"Copying and pasting? We've got you covered! You can find the full source code of this tutorial here.",source:"@site/docs/Tutorials/UserInterface/Core/Component.mdx",sourceDirName:"Tutorials/UserInterface/Core",slug:"/Tutorials/UserInterface/Core/Component",permalink:"/Tutorials/UserInterface/Core/Component",draft:!1,tags:[],version:"current",frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"UserInterface",permalink:"/Tutorials/UserInterface/"},next:{title:"ClassificationsTree",permalink:"/Tutorials/UserInterface/OBC/ClassificationsTree"}},p={},u=[{value:"Leveling up your app with custom components! \ud83d\udd0c",id:"leveling-up-your-app-with-custom-components-",level:2},{value:"Creating an stateless component",id:"creating-an-stateless-component",level:3},{value:"Creating a statefull component",id:"creating-a-statefull-component",level:3},{value:"Nesting components",id:"nesting-components",level:3}],m={toc:u},d="wrapper";function h(e){var t=e.components,n=(0,o.Z)(e,r);return(0,i.kt)(d,(0,a.Z)({},m,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("admonition",{title:"Source",type:"info"},(0,i.kt)("p",{parentName:"admonition"},"Copying and pasting? We've got you covered! You can find the full source code of this tutorial ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/ThatOpen/engine_ui-components/blob/main/packages/core/src/core/Component/example.ts"},"here"),".")),(0,i.kt)("h2",{id:"leveling-up-your-app-with-custom-components-"},"Leveling up your app with custom components! \ud83d\udd0c"),(0,i.kt)("hr",null),(0,i.kt)("p",null,"One of the greatest things about the library is that you can create your own reactive and non reactive elements (statefull and stateless components respectively) in a very simple and efficient way, all thanks to the power of ",(0,i.kt)("a",{parentName:"p",href:"https://lit.dev/docs/libraries/standalone-templates/"},"lit-html")," \ud83d\udcaa.\nThe UIComponent class has a static method to create functional components (UI defined as a function) that can be updated anytime. The method is ",(0,i.kt)("inlineCode",{parentName:"p"},"UIComponent.create"),"."),(0,i.kt)("admonition",{type:"note"},(0,i.kt)("p",{parentName:"admonition"},"Despite the UIComponent 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.")),(0,i.kt)("h3",{id:"creating-an-stateless-component"},"Creating an stateless component"),(0,i.kt)("p",null,"To start learning how to create custom components, let's create a custom component that uses the panel section:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-js"},'const statelessPanelSection = BUI.Component.create(() => {\n return BUI.html`\n \n \n \n `;\n});\n')),(0,i.kt)("admonition",{type:"warning"},(0,i.kt)("p",{parentName:"admonition"},"Remember to first call ",(0,i.kt)("inlineCode",{parentName:"p"},"UIManager.init()")," before anything else!")),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"UIComponent.create")," requires you to provide a function declaration that returns an HTML string made with the ",(0,i.kt)("inlineCode",{parentName:"p"},"html")," tag function, and the result of the function is the HTMLElement it self. "),(0,i.kt)("admonition",{type:"note"},(0,i.kt)("p",{parentName:"admonition"},"Tag functions are special declarations that are always set before a template literals to process the string.")),(0,i.kt)("p",null,"Did you notice the component is named ",(0,i.kt)("inlineCode",{parentName:"p"},"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).\nThe 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.\nThe component we just created is stateless, because it doesn't have any state in which its data depends on. "),(0,i.kt)("h3",{id:"creating-a-statefull-component"},"Creating a statefull component"),(0,i.kt)("p",null,"Now, let's take a look at how to create a component that can be updated based on state changes:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-js"},'interface PanelSectionUIState {\n label: string;\n counter: number;\n}\n\nconst [statefullPanelSection, updateStatefullPanelSection] =\n BUI.Component.create(\n (state: PanelSectionUIState) => {\n const { label, counter } = state;\n const msg = `This panel section has been updated ${counter} ${counter === 1 ? "time" : "times"}`;\n return BUI.html`\n \n \n \n `;\n },\n { label: "Statefull Panel Section", counter: 0 },\n );\n')),(0,i.kt)("p",null,"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:"),(0,i.kt)("ol",null,(0,i.kt)("li",{parentName:"ol"},"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."),(0,i.kt)("li",{parentName:"ol"},"When the component is statefull, ",(0,i.kt)("inlineCode",{parentName:"li"},"UIComponent.create")," must have a second argument to specify the initial state of the component."),(0,i.kt)("li",{parentName:"ol"},"Now, ",(0,i.kt)("inlineCode",{parentName:"li"},"UIComponent.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.",(0,i.kt)("admonition",{parentName:"li",type:"note"},(0,i.kt)("p",{parentName:"admonition"},"As for now, a statefull component can't update itself! However, you can nest other components that updates the state of some other.")))),(0,i.kt)("h3",{id:"nesting-components"},"Nesting components"),(0,i.kt)("p",null,"Now, in order to see the two components in action, let's create a third component to integrate (nest) the two previous:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-js"},'const panel = BUI.Component.create(() => {\n let counter = 0;\n const onUpdateBtnClick = () => {\n counter++;\n if (counter >= 5) {\n updateStatefullPanelSection({\n label: "Powered Statefull Panel Section \ud83d\udcaa",\n counter,\n });\n } else {\n updateStatefullPanelSection({ counter });\n }\n };\n\n return BUI.html`\n \n \n \n \n ${statelessPanelSection}\n ${statefullPanelSection}\n \n `;\n});\n')),(0,i.kt)("p",null,"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 ",(0,i.kt)("inlineCode",{parentName:"p"},"bim-button")," in order to update the state of the statefullPanelSection we created earlier. A couple of things to notice here: "),(0,i.kt)("ol",null,(0,i.kt)("li",{parentName:"ol"},"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."),(0,i.kt)("li",{parentName:"ol"},"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."),(0,i.kt)("li",{parentName:"ol"},"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."),(0,i.kt)("li",{parentName:"ol"},"You see how we integrated the two previous components into the panel? Yes, its as easy as adding them as an expression (",(0,i.kt)("inlineCode",{parentName:"li"},"${statelessPanelSection}")," and ",(0,i.kt)("inlineCode",{parentName:"li"},"${statefullPanelSection}")," in this case).",(0,i.kt)("admonition",{parentName:"li",type:"tip"},(0,i.kt)("p",{parentName:"admonition"},"In order to know the syntax you can write inside the template literal tagged by the html function, look at the ",(0,i.kt)("a",{parentName:"p",href:"https://lit.dev/docs/templates/overview/"},"lit-html")," documentation.")),"Finally, you can add your panel component anywhere you want as its an HTMLElement just like any other!")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-js"},"document.body.append(panel);\n")),(0,i.kt)("p",null,"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 \ud83d\ude42."),(0,i.kt)("admonition",{type:"tip"},(0,i.kt)("p",{parentName:"admonition"},"The complementary packages of the library such as ",(0,i.kt)("inlineCode",{parentName:"p"},"@thatopen/ui-components-obc")," or ",(0,i.kt)("inlineCode",{parentName:"p"},"@thatopen/ui-components-three")," 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 \ud83d\ude09 ")),(0,i.kt)("iframe",{src:"https://thatopen.github.io/engine_ui-components/examples/Component"}))}h.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkdocs=self.webpackChunkdocs||[]).push([[9311],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>h});var a=n(7294);function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function r(e){for(var t=1;t=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var l=a.createContext({}),c=function(e){var t=a.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):r(r({},t),e)),n},p=function(e){var t=c(e.components);return a.createElement(l.Provider,{value:t},e.children)},u="mdxType",m={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},d=a.forwardRef((function(e,t){var n=e.components,o=e.mdxType,i=e.originalType,l=e.parentName,p=s(e,["components","mdxType","originalType","parentName"]),u=c(n),d=o,h=u["".concat(l,".").concat(d)]||u[d]||m[d]||i;return n?a.createElement(h,r(r({ref:t},p),{},{components:n})):a.createElement(h,r({ref:t},p))}));function h(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var i=n.length,r=new Array(i);r[0]=d;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[u]="string"==typeof e?e:o,r[1]=s;for(var c=2;c{n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>l,default:()=>h,frontMatter:()=>s,metadata:()=>c,toc:()=>u});var a=n(7462),o=n(3366),i=(n(7294),n(3905)),r=["components"],s={},l=void 0,c={unversionedId:"Tutorials/UserInterface/Core/Component",id:"Tutorials/UserInterface/Core/Component",title:"Component",description:"Copying and pasting? We've got you covered! You can find the full source code of this tutorial here.",source:"@site/docs/Tutorials/UserInterface/Core/Component.mdx",sourceDirName:"Tutorials/UserInterface/Core",slug:"/Tutorials/UserInterface/Core/Component",permalink:"/Tutorials/UserInterface/Core/Component",draft:!1,tags:[],version:"current",frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"UserInterface",permalink:"/Tutorials/UserInterface/"},next:{title:"ClassificationsTree",permalink:"/Tutorials/UserInterface/OBC/ClassificationsTree"}},p={},u=[{value:"Leveling up your app with custom components! \ud83d\udd0c",id:"leveling-up-your-app-with-custom-components-",level:2},{value:"Creating an stateless component",id:"creating-an-stateless-component",level:3},{value:"Creating a statefull component",id:"creating-a-statefull-component",level:3},{value:"Nesting components",id:"nesting-components",level:3}],m={toc:u},d="wrapper";function h(e){var t=e.components,n=(0,o.Z)(e,r);return(0,i.kt)(d,(0,a.Z)({},m,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("admonition",{title:"Source",type:"info"},(0,i.kt)("p",{parentName:"admonition"},"Copying and pasting? We've got you covered! You can find the full source code of this tutorial ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/ThatOpen/engine_ui-components/blob/main/packages/core/src/core/Component/example.ts"},"here"),".")),(0,i.kt)("h2",{id:"leveling-up-your-app-with-custom-components-"},"Leveling up your app with custom components! \ud83d\udd0c"),(0,i.kt)("hr",null),(0,i.kt)("p",null,"One of the greatest things about the library is that you can create your own reactive and non reactive elements (statefull and stateless components respectively) in a very simple and efficient way, all thanks to the power of ",(0,i.kt)("a",{parentName:"p",href:"https://lit.dev/docs/libraries/standalone-templates/"},"lit-html")," \ud83d\udcaa.\nThe UIComponent class has a static method to create functional components (UI defined as a function) that can be updated anytime. The method is ",(0,i.kt)("inlineCode",{parentName:"p"},"UIComponent.create"),"."),(0,i.kt)("admonition",{type:"note"},(0,i.kt)("p",{parentName:"admonition"},"Despite the UIComponent 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.")),(0,i.kt)("h3",{id:"creating-an-stateless-component"},"Creating an stateless component"),(0,i.kt)("p",null,"To start learning how to create custom components, let's create a custom component that uses the panel section:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-js"},'const statelessPanelSection = BUI.Component.create(() => {\n return BUI.html`\n \n \n \n `;\n});\n')),(0,i.kt)("admonition",{type:"warning"},(0,i.kt)("p",{parentName:"admonition"},"Remember to first call ",(0,i.kt)("inlineCode",{parentName:"p"},"UIManager.init()")," before anything else!")),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"UIComponent.create")," requires you to provide a function declaration that returns an HTML string made with the ",(0,i.kt)("inlineCode",{parentName:"p"},"html")," tag function, and the result of the function is the HTMLElement it self. "),(0,i.kt)("admonition",{type:"note"},(0,i.kt)("p",{parentName:"admonition"},"Tag functions are special declarations that are always set before a template literals to process the string.")),(0,i.kt)("p",null,"Did you notice the component is named ",(0,i.kt)("inlineCode",{parentName:"p"},"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).\nThe 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.\nThe component we just created is stateless, because it doesn't have any state in which its data depends on. "),(0,i.kt)("h3",{id:"creating-a-statefull-component"},"Creating a statefull component"),(0,i.kt)("p",null,"Now, let's take a look at how to create a component that can be updated based on state changes:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-js"},'interface PanelSectionUIState {\n label: string;\n counter: number;\n}\n\nconst [statefullPanelSection, updateStatefullPanelSection] =\n BUI.Component.create(\n (state: PanelSectionUIState) => {\n const { label, counter } = state;\n const msg = `This panel section has been updated ${counter} ${counter === 1 ? "time" : "times"}`;\n return BUI.html`\n \n ${msg}\n \n `;\n },\n { label: "Statefull Panel Section", counter: 0 },\n );\n')),(0,i.kt)("p",null,"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:"),(0,i.kt)("ol",null,(0,i.kt)("li",{parentName:"ol"},"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."),(0,i.kt)("li",{parentName:"ol"},"When the component is statefull, ",(0,i.kt)("inlineCode",{parentName:"li"},"UIComponent.create")," must have a second argument to specify the initial state of the component."),(0,i.kt)("li",{parentName:"ol"},"Now, ",(0,i.kt)("inlineCode",{parentName:"li"},"UIComponent.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.",(0,i.kt)("admonition",{parentName:"li",type:"note"},(0,i.kt)("p",{parentName:"admonition"},"As for now, a statefull component can't update itself! However, you can nest other components that updates the state of some other.")))),(0,i.kt)("h3",{id:"nesting-components"},"Nesting components"),(0,i.kt)("p",null,"Now, in order to see the two components in action, let's create a third component to integrate (nest) the two previous:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-js"},'const panel = BUI.Component.create(() => {\n let counter = 0;\n const onUpdateBtnClick = () => {\n counter++;\n if (counter >= 5) {\n updateStatefullPanelSection({\n label: "Powered Statefull Panel Section \ud83d\udcaa",\n counter,\n });\n } else {\n updateStatefullPanelSection({ counter });\n }\n };\n\n return BUI.html`\n \n \n \n \n ${statelessPanelSection}\n ${statefullPanelSection}\n \n `;\n});\n')),(0,i.kt)("p",null,"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 ",(0,i.kt)("inlineCode",{parentName:"p"},"bim-button")," in order to update the state of the statefullPanelSection we created earlier. A couple of things to notice here: "),(0,i.kt)("ol",null,(0,i.kt)("li",{parentName:"ol"},"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."),(0,i.kt)("li",{parentName:"ol"},"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."),(0,i.kt)("li",{parentName:"ol"},"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."),(0,i.kt)("li",{parentName:"ol"},"You see how we integrated the two previous components into the panel? Yes, its as easy as adding them as an expression (",(0,i.kt)("inlineCode",{parentName:"li"},"${statelessPanelSection}")," and ",(0,i.kt)("inlineCode",{parentName:"li"},"${statefullPanelSection}")," in this case).",(0,i.kt)("admonition",{parentName:"li",type:"tip"},(0,i.kt)("p",{parentName:"admonition"},"In order to know the syntax you can write inside the template literal tagged by the html function, look at the ",(0,i.kt)("a",{parentName:"p",href:"https://lit.dev/docs/templates/overview/"},"lit-html")," documentation.")),"Finally, you can add your panel component anywhere you want as its an HTMLElement just like any other!")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-js"},"document.body.append(panel);\n")),(0,i.kt)("p",null,"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 \ud83d\ude42."),(0,i.kt)("admonition",{type:"tip"},(0,i.kt)("p",{parentName:"admonition"},"The complementary packages of the library such as ",(0,i.kt)("inlineCode",{parentName:"p"},"@thatopen/ui-components-obc")," or ",(0,i.kt)("inlineCode",{parentName:"p"},"@thatopen/ui-components-three")," 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 \ud83d\ude09 ")),(0,i.kt)("iframe",{src:"https://thatopen.github.io/engine_ui-components/examples/Component"}))}h.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/build/assets/js/e3702cd4.dc14a769.js b/build/assets/js/e3702cd4.dc14a769.js deleted file mode 100644 index 0a9dc5a57..000000000 --- a/build/assets/js/e3702cd4.dc14a769.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkdocs=self.webpackChunkdocs||[]).push([[2232],{3905:(e,t,n)=>{n.d(t,{Zo:()=>m,kt:()=>h});var a=n(7294);function o(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 a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function s(e){for(var t=1;t=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var p=a.createContext({}),l=function(e){var t=a.useContext(p),n=t;return e&&(n="function"==typeof e?e(t):s(s({},t),e)),n},m=function(e){var t=l(e.components);return a.createElement(p.Provider,{value:t},e.children)},c="mdxType",u={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},d=a.forwardRef((function(e,t){var n=e.components,o=e.mdxType,r=e.originalType,p=e.parentName,m=i(e,["components","mdxType","originalType","parentName"]),c=l(n),d=o,h=c["".concat(p,".").concat(d)]||c[d]||u[d]||r;return n?a.createElement(h,s(s({ref:t},m),{},{components:n})):a.createElement(h,s({ref:t},m))}));function h(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var r=n.length,s=new Array(r);s[0]=d;var i={};for(var p in t)hasOwnProperty.call(t,p)&&(i[p]=t[p]);i.originalType=e,i[c]="string"==typeof e?e:o,s[1]=i;for(var l=2;l{n.r(t),n.d(t,{assets:()=>m,contentTitle:()=>p,default:()=>h,frontMatter:()=>i,metadata:()=>l,toc:()=>c});var a=n(7462),o=n(3366),r=(n(7294),n(3905)),s=["components"],i={sidebar_position:3},p="\ud83e\uddf9 Keeping them clean",l={unversionedId:"components/clean-components-guide",id:"components/clean-components-guide",title:"\ud83e\uddf9 Keeping them clean",description:"\ud83e\uddfd Basics",source:"@site/docs/components/clean-components-guide.md",sourceDirName:"components",slug:"/components/clean-components-guide",permalink:"/components/clean-components-guide",draft:!1,tags:[],version:"current",sidebarPosition:3,frontMatter:{sidebar_position:3},sidebar:"tutorialSidebar",previous:{title:"\ud83e\uddbe Making your own",permalink:"/components/creating-components"},next:{title:"\ud83e\udded Tutorial paths",permalink:"/components/tutorial-paths"}},m={},c=[{value:"\ud83e\uddfd Basics",id:"-basics",level:2},{value:"\ud83e\uddfc TypeScript",id:"-typescript",level:2},{value:"\ud83d\udcda Documentation",id:"-documentation",level:2},{value:"\ud83e\udde0 Memory management",id:"-memory-management",level:2},{value:"\ud83e\udd4e 3D objects and materials",id:"-3d-objects-and-materials",level:3},{value:"\ud83d\udcc5 Events",id:"-events",level:3},{value:"\ud83d\udc18 Huge objects / arrays",id:"-huge-objects--arrays",level:3}],u={toc:c},d="wrapper";function h(e){var t=e.components,n=(0,o.Z)(e,s);return(0,r.kt)(d,(0,a.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"-keeping-them-clean"},"\ud83e\uddf9 Keeping them clean"),(0,r.kt)("h2",{id:"-basics"},"\ud83e\uddfd Basics"),(0,r.kt)("p",null,"Always extend from the base ",(0,r.kt)("inlineCode",{parentName:"p"},"Component")," class. \ud83d\udc6a"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-tsx"},'import * as OBC from "@thatopen/components"\n\nexport MyComponent extends OBC.Component {\n // ...\n}\n')),(0,r.kt)("p",null,"Always name the ",(0,r.kt)("strong",{parentName:"p"},"base file")," of your component ",(0,r.kt)("inlineCode",{parentName:"p"},"index.ts")," and store it in ",(0,r.kt)("strong",{parentName:"p"},"a folder with the component name"),". If you need to include other supporting files, create a ",(0,r.kt)("inlineCode",{parentName:"p"},"src")," folder in the component folder. You can call those supporting file whatever you want. This is a basic folder structure: \ud83d\uddc3\ufe0f"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("p",{parentName:"li"},"MyComponent"),(0,r.kt)("ul",{parentName:"li"},(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("p",{parentName:"li"},"src"),(0,r.kt)("ul",{parentName:"li"},(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("p",{parentName:"li"},"supporting-file-1.ts")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("p",{parentName:"li"},"supporting-file-2.ts")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("p",{parentName:"li"},"index.ts"))))))),(0,r.kt)("h2",{id:"-typescript"},"\ud83e\uddfc TypeScript"),(0,r.kt)("p",null,"Follow the ",(0,r.kt)("strong",{parentName:"p"},"Single Responsibility Principle"),". \ud83e\udd47"),(0,r.kt)("p",null,"Always name private members with ",(0,r.kt)("strong",{parentName:"p"},"underscore"),". \ud83e\udd77\ud83c\udffb"),(0,r.kt)("p",null,"Avoid using ",(0,r.kt)("inlineCode",{parentName:"p"},"!")," in property fields. If a property element is not initialized in the constructor, you can either use ",(0,r.kt)("inlineCode",{parentName:"p"},"?"),", or ",(0,r.kt)("strong",{parentName:"p"},"create a getter")," to assert that it exists before getting it, like this:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-tsx"},'private _customProperty?: string;\n\nget customProperty(): string { \n if(!this._customProperty) {\n throw new Error("Custom property not initialized!");\n }\n return this._customProperty;\n }\n\n')),(0,r.kt)("p",null,"Avoid using ",(0,r.kt)("inlineCode",{parentName:"p"},"any")," as much as possible. \u274c"),(0,r.kt)("p",null,"Never define private properties in the constructor. ",(0,r.kt)("strong",{parentName:"p"},"Make them explicit")," beforehand: \ud83d\udccb"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-tsx"},"/*Incorrect*/\nconstructor(private _components: Components)\n\n/*Correct*/\nprivate _components: Components\nconstructor(components: Components) {\n this._components = components;\n}\n")),(0,r.kt)("p",null,"Always ",(0,r.kt)("strong",{parentName:"p"},"make events readonly")," and initialize them directly. \u26a1"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-jsx"},"readonly onCreated = new OBC.Event()\n")),(0,r.kt)("p",null,"Always make sure to ",(0,r.kt)("strong",{parentName:"p"},"know the interfaces")," you can implement when creating your component (i.e. ",(0,r.kt)("inlineCode",{parentName:"p"},"Creatable"),", ",(0,r.kt)("inlineCode",{parentName:"p"},"Hideable"),", ",(0,r.kt)("inlineCode",{parentName:"p"},"UI"),", etc), that way we keep things uniform in terms of properties and methods naming and types."),(0,r.kt)("h2",{id:"-documentation"},"\ud83d\udcda Documentation"),(0,r.kt)("p",null,"In tutorials, ",(0,r.kt)("strong",{parentName:"p"},"try to not reference specifics")," inside paragraphs. That allows to easily update the tutorial code without need to also update the paragraphs. \ud83d\udc47\ud83c\udffb"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-tsx"},"/*\u274c Incorrect */\n/*MD\nTo add a cube to the scene, you need to call scene.add()\n*/\n\nscene.add(cube)\n\n/*\u2705 Correct*/\n/*MD\nTo add a cube to the scene, just add the following code line!\n*/\n\nscene.add(cube)\n")),(0,r.kt)("h2",{id:"-memory-management"},"\ud83e\udde0 Memory management"),(0,r.kt)("p",null,(0,r.kt)("strong",{parentName:"p"},"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 ",(0,r.kt)("strong",{parentName:"p"},"freezes / crashes"),". This is especially relevant when using SPA (Single Page Application) libraries and frameworks, like ",(0,r.kt)("strong",{parentName:"p"},"React"),", ",(0,r.kt)("strong",{parentName:"p"},"Angular"),", ",(0,r.kt)("strong",{parentName:"p"},"Vue"),", etc. \ud83d\uded1"),(0,r.kt)("p",null,"To make sure your component ",(0,r.kt)("strong",{parentName:"p"},"doesn\u2019t cause memory leaks")," and stays efficient, you need to make sure that:"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("p",{parentName:"li"},"It is added to the ",(0,r.kt)("inlineCode",{parentName:"p"},"components")," instance in the constructor like specified in the ",(0,r.kt)("a",{parentName:"p",href:"/components/creating-components#-create-it"},"component creation guide"),".")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("p",{parentName:"li"},"It implements the ",(0,r.kt)("inlineCode",{parentName:"p"},"Disposable")," interface. This will force you to implement a ",(0,r.kt)("inlineCode",{parentName:"p"},"dispose()")," method that will be called automatically by the library when ",(0,r.kt)("inlineCode",{parentName:"p"},"components.dispose()")," is called. You can ",(0,r.kt)("strong",{parentName:"p"},"add all the clean up logic inside this method"),". You also need to add a ",(0,r.kt)("inlineCode",{parentName:"p"},"onDisposed")," event so that you can subscribe to the deletion of that component to clean up. "))),(0,r.kt)("p",null,"There are some things that you want to clean up inside the ",(0,r.kt)("inlineCode",{parentName:"p"},"dispose")," method:"),(0,r.kt)("h3",{id:"-3d-objects-and-materials"},"\ud83e\udd4e 3D objects and materials"),(0,r.kt)("p",null,"Three.js needs to ",(0,r.kt)("a",{parentName:"p",href:"https://threejs.org/docs/#manual/en/introduction/How-to-dispose-of-objects"},"manually release the memory")," of the ",(0,r.kt)("strong",{parentName:"p"},"geometry")," and ",(0,r.kt)("strong",{parentName:"p"},"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:"),(0,r.kt)("ol",null,(0,r.kt)("li",{parentName:"ol"},(0,r.kt)("p",{parentName:"li"},"\ud83e\uddf9 Using the Three.js ",(0,r.kt)("inlineCode",{parentName:"p"},"dispose")," method to ",(0,r.kt)("strong",{parentName:"p"},"delete all geometries and materials"),", including their children.")),(0,r.kt)("li",{parentName:"ol"},(0,r.kt)("p",{parentName:"li"},"\ud83e\uddf9 Using the ",(0,r.kt)("inlineCode",{parentName:"p"},"Disposer")," component provided by the components library, which ",(0,r.kt)("strong",{parentName:"p"},"does everything for you"),". "))),(0,r.kt)("p",null,"To make sure that the browser gets rid of this memory, you should also ",(0,r.kt)("strong",{parentName:"p"},"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 ",(0,r.kt)("inlineCode",{parentName:"p"},"meshes"),", you can get rid of it like this: \ud83d\udc47\ud83c\udffb"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-tsx"},'import * as THREE from "three";\nimport * as OBC from "@thatopen/components";\n\nclass YourComponent extends Component implements Disposable {\n\n // ...\n\n readonly onDisposed = new OBC.Event();\n\n private _meshes: Mesh[];\n\n dispose() {\n // ...\n const disposer = this.components.get(OBC.Disposer);\n for(const mesh of this.meshes) {\n // The disposer gets rid of geometries and materials\n // including children\n disposer.dispose(mesh);\n }\n // Removing all references to them\n // in arrays an object is critical for this to work\n this._meshes = [];\n this.onDisposed.trigger();\n this.onDisposed.reset();\n }\n\n}\n')),(0,r.kt)("h3",{id:"-events"},"\ud83d\udcc5 Events"),(0,r.kt)("p",null,"Events are a nice way of ",(0,r.kt)("strong",{parentName:"p"},"binding HTML elements to JS logic"),". A common way of doing that is using ",(0,r.kt)("inlineCode",{parentName:"p"},"addEventListener"),". That\u2019s fine if all the events are bound to HTML elements that you create inside your component and ",(0,r.kt)("strong",{parentName:"p"},"are destroyed when your component is disposed"),". \ud83d\udc4c\ud83c\udffb"),(0,r.kt)("p",null,"But in some situations you\u2019ll need to add events to HTML elements outside your components, or even to the global ",(0,r.kt)("inlineCode",{parentName:"p"},"window")," object. In those cases, you will need to make sure that you ",(0,r.kt)("strong",{parentName:"p"},"get rid of these events")," when your component is disposed. You can do that with ",(0,r.kt)("inlineCode",{parentName:"p"},"removeEventListener"),", and making sure that you keep a reference to the logic as an ",(0,r.kt)("strong",{parentName:"p"},"arrow function"),". \ud83c\udff9"),(0,r.kt)("p",null,"To make sure you don\u2019t forget about ",(0,r.kt)("strong",{parentName:"p"},"getting rid of your events"),", it\u2019s a good practice to create a ",(0,r.kt)("inlineCode",{parentName:"p"},"setupEvents")," method that allows you to toggle them like this: \ud83d\udc47\ud83c\udffb"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-tsx"},'import * as THREE from "three";\nimport * as OBC from "@thatopen/components";\n\nclass YourComponent extends Component implements Disposable {\n\n // ...\n\n constructor() {\n this.setupEvents(true);\n }\n\n dispose() {\n // ...\n this.setupEvents(false);\n // ...\n }\n\n private setupEvents(active: boolean) {\n if(active) {\n window.addEventListener("mousemove", this.logMessage);\n } else {\n window.removeEventListener("mousemove", this.logMessage);\n }\n }\n\n private logMessage = () => {\n console.log("Hey!");\n }\n}\n')),(0,r.kt)("h3",{id:"-huge-objects--arrays"},"\ud83d\udc18 Huge objects / arrays"),(0,r.kt)("p",null,"Some components are data-heavy. JavaScript has an automatic garbage collector that should take care of these, but that can take some time. To ",(0,r.kt)("strong",{parentName:"p"},"accelerate this release of memory"),", you can just assign them an empty value:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-jsx"},'import * as THREE from "three";\nimport * as OBC from "@thatopen/components";\n\nclass YourComponent extends Component implements Disposable {\n\n dataArray: any = [];\n dataObject: any = {};\n\n dispose() {\n // ...\n this.dataArray= [];\n this.dataObject= {};\n }\n}\n')))}h.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/build/assets/js/e3702cd4.ef6024b3.js b/build/assets/js/e3702cd4.ef6024b3.js new file mode 100644 index 000000000..7b7e10073 --- /dev/null +++ b/build/assets/js/e3702cd4.ef6024b3.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkdocs=self.webpackChunkdocs||[]).push([[2232],{3905:(e,t,n)=>{n.d(t,{Zo:()=>m,kt:()=>h});var a=n(7294);function o(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 a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function s(e){for(var t=1;t=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var p=a.createContext({}),l=function(e){var t=a.useContext(p),n=t;return e&&(n="function"==typeof e?e(t):s(s({},t),e)),n},m=function(e){var t=l(e.components);return a.createElement(p.Provider,{value:t},e.children)},c="mdxType",u={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},d=a.forwardRef((function(e,t){var n=e.components,o=e.mdxType,r=e.originalType,p=e.parentName,m=i(e,["components","mdxType","originalType","parentName"]),c=l(n),d=o,h=c["".concat(p,".").concat(d)]||c[d]||u[d]||r;return n?a.createElement(h,s(s({ref:t},m),{},{components:n})):a.createElement(h,s({ref:t},m))}));function h(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var r=n.length,s=new Array(r);s[0]=d;var i={};for(var p in t)hasOwnProperty.call(t,p)&&(i[p]=t[p]);i.originalType=e,i[c]="string"==typeof e?e:o,s[1]=i;for(var l=2;l{n.r(t),n.d(t,{assets:()=>m,contentTitle:()=>p,default:()=>h,frontMatter:()=>i,metadata:()=>l,toc:()=>c});var a=n(7462),o=n(3366),r=(n(7294),n(3905)),s=["components"],i={sidebar_position:3},p="\ud83e\uddf9 Keeping them clean",l={unversionedId:"components/clean-components-guide",id:"components/clean-components-guide",title:"\ud83e\uddf9 Keeping them clean",description:"\ud83e\uddfd Basics",source:"@site/docs/components/clean-components-guide.md",sourceDirName:"components",slug:"/components/clean-components-guide",permalink:"/components/clean-components-guide",draft:!1,tags:[],version:"current",sidebarPosition:3,frontMatter:{sidebar_position:3},sidebar:"tutorialSidebar",previous:{title:"\ud83e\uddbe Making your own",permalink:"/components/creating-components"},next:{title:"\ud83e\udded Tutorial paths",permalink:"/components/tutorial-paths"}},m={},c=[{value:"\ud83e\uddfd Basics",id:"-basics",level:2},{value:"\ud83e\uddfc TypeScript",id:"-typescript",level:2},{value:"\ud83d\udcda Documentation",id:"-documentation",level:2},{value:"\ud83e\udde0 Memory management",id:"-memory-management",level:2},{value:"\ud83e\udd4e 3D objects and materials",id:"-3d-objects-and-materials",level:3},{value:"\ud83d\udcc5 Events",id:"-events",level:3},{value:"\ud83d\udc18 Huge objects / arrays",id:"-huge-objects--arrays",level:3}],u={toc:c},d="wrapper";function h(e){var t=e.components,n=(0,o.Z)(e,s);return(0,r.kt)(d,(0,a.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"-keeping-them-clean"},"\ud83e\uddf9 Keeping them clean"),(0,r.kt)("h2",{id:"-basics"},"\ud83e\uddfd Basics"),(0,r.kt)("p",null,"Always extend from the base ",(0,r.kt)("inlineCode",{parentName:"p"},"Component")," class. \ud83d\udc6a"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-tsx"},'import * as OBC from "@thatopen/components"\n\nexport MyComponent extends OBC.Component {\n // ...\n}\n')),(0,r.kt)("p",null,"Always name the ",(0,r.kt)("strong",{parentName:"p"},"base file")," of your component ",(0,r.kt)("inlineCode",{parentName:"p"},"index.ts")," and store it in ",(0,r.kt)("strong",{parentName:"p"},"a folder with the component name"),". If you need to include other supporting files, create a ",(0,r.kt)("inlineCode",{parentName:"p"},"src")," folder in the component folder. You can call those supporting file whatever you want. The folder should have another ",(0,r.kt)("inlineCode",{parentName:"p"},"index.ts")," file exporting all the elements that have to be exported from the folder. This is a basic folder structure: \ud83d\uddc3\ufe0f"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("p",{parentName:"li"},"MyComponent"),(0,r.kt)("ul",{parentName:"li"},(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("p",{parentName:"li"},"index.ts")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("p",{parentName:"li"},"src"),(0,r.kt)("ul",{parentName:"li"},(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("p",{parentName:"li"},"supporting-file-1.ts")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("p",{parentName:"li"},"supporting-file-2.ts")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("p",{parentName:"li"},"index.ts"))))))),(0,r.kt)("h2",{id:"-typescript"},"\ud83e\uddfc TypeScript"),(0,r.kt)("p",null,"Follow the ",(0,r.kt)("strong",{parentName:"p"},"Single Responsibility Principle"),". \ud83e\udd47"),(0,r.kt)("p",null,"Always name private members with ",(0,r.kt)("strong",{parentName:"p"},"underscore"),". \ud83e\udd77\ud83c\udffb"),(0,r.kt)("p",null,"Avoid using ",(0,r.kt)("inlineCode",{parentName:"p"},"!")," in property fields. If a property element is not initialized in the constructor, you can either use ",(0,r.kt)("inlineCode",{parentName:"p"},"?"),", or ",(0,r.kt)("strong",{parentName:"p"},"create a getter")," to assert that it exists before getting it, like this:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-tsx"},'private _customProperty?: string;\n\nget customProperty(): string { \n if(!this._customProperty) {\n throw new Error("Custom property not initialized!");\n }\n return this._customProperty;\n }\n\n')),(0,r.kt)("p",null,"Avoid using ",(0,r.kt)("inlineCode",{parentName:"p"},"any")," as much as possible. \u274c"),(0,r.kt)("p",null,"Never define private properties in the constructor. ",(0,r.kt)("strong",{parentName:"p"},"Make them explicit")," beforehand: \ud83d\udccb"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-tsx"},"/*Incorrect*/\nconstructor(private _components: Components)\n\n/*Correct*/\nprivate _components: Components\nconstructor(components: Components) {\n this._components = components;\n}\n")),(0,r.kt)("p",null,"Always ",(0,r.kt)("strong",{parentName:"p"},"make events readonly")," and initialize them directly. \u26a1"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-jsx"},"readonly onCreated = new OBC.Event()\n")),(0,r.kt)("p",null,"Always make sure to ",(0,r.kt)("strong",{parentName:"p"},"know the interfaces")," you can implement when creating your component (i.e. ",(0,r.kt)("inlineCode",{parentName:"p"},"Creatable"),", ",(0,r.kt)("inlineCode",{parentName:"p"},"Hideable"),", ",(0,r.kt)("inlineCode",{parentName:"p"},"UI"),", etc), that way we keep things uniform in terms of properties and methods naming and types."),(0,r.kt)("h2",{id:"-documentation"},"\ud83d\udcda Documentation"),(0,r.kt)("p",null,"In tutorials, ",(0,r.kt)("strong",{parentName:"p"},"try to not reference specifics")," inside paragraphs. That allows to easily update the tutorial code without need to also update the paragraphs. \ud83d\udc47\ud83c\udffb"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-tsx"},"/*\u274c Incorrect */\n/*MD\nTo add a cube to the scene, you need to call scene.add()\n*/\n\nscene.add(cube)\n\n/*\u2705 Correct*/\n/*MD\nTo add a cube to the scene, just add the following code line!\n*/\n\nscene.add(cube)\n")),(0,r.kt)("h2",{id:"-memory-management"},"\ud83e\udde0 Memory management"),(0,r.kt)("p",null,(0,r.kt)("strong",{parentName:"p"},"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 ",(0,r.kt)("strong",{parentName:"p"},"freezes / crashes"),". This is especially relevant when using SPA (Single Page Application) libraries and frameworks, like ",(0,r.kt)("strong",{parentName:"p"},"React"),", ",(0,r.kt)("strong",{parentName:"p"},"Angular"),", ",(0,r.kt)("strong",{parentName:"p"},"Vue"),", etc. \ud83d\uded1"),(0,r.kt)("p",null,"To make sure your component ",(0,r.kt)("strong",{parentName:"p"},"doesn\u2019t cause memory leaks")," and stays efficient, you need to make sure that:"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("p",{parentName:"li"},"It is added to the ",(0,r.kt)("inlineCode",{parentName:"p"},"components")," instance in the constructor like specified in the ",(0,r.kt)("a",{parentName:"p",href:"/components/creating-components#-create-it"},"component creation guide"),".")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("p",{parentName:"li"},"It implements the ",(0,r.kt)("inlineCode",{parentName:"p"},"Disposable")," interface. This will force you to implement a ",(0,r.kt)("inlineCode",{parentName:"p"},"dispose()")," method that will be called automatically by the library when ",(0,r.kt)("inlineCode",{parentName:"p"},"components.dispose()")," is called. You can ",(0,r.kt)("strong",{parentName:"p"},"add all the clean up logic inside this method"),". You also need to add a ",(0,r.kt)("inlineCode",{parentName:"p"},"onDisposed")," event so that you can subscribe to the deletion of that component to clean up. "))),(0,r.kt)("p",null,"There are some things that you want to clean up inside the ",(0,r.kt)("inlineCode",{parentName:"p"},"dispose")," method:"),(0,r.kt)("h3",{id:"-3d-objects-and-materials"},"\ud83e\udd4e 3D objects and materials"),(0,r.kt)("p",null,"Three.js needs to ",(0,r.kt)("a",{parentName:"p",href:"https://threejs.org/docs/#manual/en/introduction/How-to-dispose-of-objects"},"manually release the memory")," of the ",(0,r.kt)("strong",{parentName:"p"},"geometry")," and ",(0,r.kt)("strong",{parentName:"p"},"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:"),(0,r.kt)("ol",null,(0,r.kt)("li",{parentName:"ol"},(0,r.kt)("p",{parentName:"li"},"\ud83e\uddf9 Using the Three.js ",(0,r.kt)("inlineCode",{parentName:"p"},"dispose")," method to ",(0,r.kt)("strong",{parentName:"p"},"delete all geometries and materials"),", including their children.")),(0,r.kt)("li",{parentName:"ol"},(0,r.kt)("p",{parentName:"li"},"\ud83e\uddf9 Using the ",(0,r.kt)("inlineCode",{parentName:"p"},"Disposer")," component provided by the components library, which ",(0,r.kt)("strong",{parentName:"p"},"does everything for you"),". "))),(0,r.kt)("p",null,"To make sure that the browser gets rid of this memory, you should also ",(0,r.kt)("strong",{parentName:"p"},"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 ",(0,r.kt)("inlineCode",{parentName:"p"},"meshes"),", you can get rid of it like this: \ud83d\udc47\ud83c\udffb"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-tsx"},'import * as THREE from "three";\nimport * as OBC from "@thatopen/components";\n\nclass YourComponent extends Component implements Disposable {\n\n // ...\n\n readonly onDisposed = new OBC.Event();\n\n private _meshes: Mesh[];\n\n dispose() {\n // ...\n const disposer = this.components.get(OBC.Disposer);\n for(const mesh of this.meshes) {\n // The disposer gets rid of geometries and materials\n // including children\n disposer.dispose(mesh);\n }\n // Removing all references to them\n // in arrays an object is critical for this to work\n this._meshes = [];\n this.onDisposed.trigger();\n this.onDisposed.reset();\n }\n\n}\n')),(0,r.kt)("h3",{id:"-events"},"\ud83d\udcc5 Events"),(0,r.kt)("p",null,"Events are a nice way of ",(0,r.kt)("strong",{parentName:"p"},"binding HTML elements to JS logic"),". A common way of doing that is using ",(0,r.kt)("inlineCode",{parentName:"p"},"addEventListener"),". That\u2019s fine if all the events are bound to HTML elements that you create inside your component and ",(0,r.kt)("strong",{parentName:"p"},"are destroyed when your component is disposed"),". \ud83d\udc4c\ud83c\udffb"),(0,r.kt)("p",null,"But in some situations you\u2019ll need to add events to HTML elements outside your components, or even to the global ",(0,r.kt)("inlineCode",{parentName:"p"},"window")," object. In those cases, you will need to make sure that you ",(0,r.kt)("strong",{parentName:"p"},"get rid of these events")," when your component is disposed. You can do that with ",(0,r.kt)("inlineCode",{parentName:"p"},"removeEventListener"),", and making sure that you keep a reference to the logic as an ",(0,r.kt)("strong",{parentName:"p"},"arrow function"),". \ud83c\udff9"),(0,r.kt)("p",null,"To make sure you don\u2019t forget about ",(0,r.kt)("strong",{parentName:"p"},"getting rid of your events"),", it\u2019s a good practice to create a ",(0,r.kt)("inlineCode",{parentName:"p"},"setupEvents")," method that allows you to toggle them like this: \ud83d\udc47\ud83c\udffb"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-tsx"},'import * as THREE from "three";\nimport * as OBC from "@thatopen/components";\n\nclass YourComponent extends Component implements Disposable {\n\n // ...\n\n constructor() {\n this.setupEvents(true);\n }\n\n dispose() {\n // ...\n this.setupEvents(false);\n // ...\n }\n\n private setupEvents(active: boolean) {\n if(active) {\n window.addEventListener("mousemove", this.logMessage);\n } else {\n window.removeEventListener("mousemove", this.logMessage);\n }\n }\n\n private logMessage = () => {\n console.log("Hey!");\n }\n}\n')),(0,r.kt)("h3",{id:"-huge-objects--arrays"},"\ud83d\udc18 Huge objects / arrays"),(0,r.kt)("p",null,"Some components are data-heavy. JavaScript has an automatic garbage collector that should take care of these, but that can take some time. To ",(0,r.kt)("strong",{parentName:"p"},"accelerate this release of memory"),", you can just assign them an empty value:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-jsx"},'import * as THREE from "three";\nimport * as OBC from "@thatopen/components";\n\nclass YourComponent extends Component implements Disposable {\n\n dataArray: any = [];\n dataObject: any = {};\n\n dispose() {\n // ...\n this.dataArray= [];\n this.dataObject= {};\n }\n}\n')))}h.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/build/assets/js/fba9713b.1f342564.js b/build/assets/js/fba9713b.1f342564.js new file mode 100644 index 000000000..04752a079 --- /dev/null +++ b/build/assets/js/fba9713b.1f342564.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkdocs=self.webpackChunkdocs||[]).push([[3132],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,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 l=o.createContext({}),c=function(e){var t=o.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},p=function(e){var t=c(e.components);return o.createElement(l.Provider,{value:t},e.children)},m="mdxType",u={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},d=o.forwardRef((function(e,t){var n=e.components,a=e.mdxType,r=e.originalType,l=e.parentName,p=s(e,["components","mdxType","originalType","parentName"]),m=c(n),d=a,h=m["".concat(l,".").concat(d)]||m[d]||u[d]||r;return n?o.createElement(h,i(i({ref:t},p),{},{components:n})):o.createElement(h,i({ref:t},p))}));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]=d;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[m]="string"==typeof e?e:a,i[1]=s;for(var c=2;c{n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>l,default:()=>h,frontMatter:()=>s,metadata:()=>c,toc:()=>m});var o=n(7462),a=n(3366),r=(n(7294),n(3905)),i=["components"],s={},l=void 0,c={unversionedId:"Tutorials/Components/Core/Hider",id:"Tutorials/Components/Core/Hider",title:"Hider",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/Hider.mdx",sourceDirName:"Tutorials/Components/Core",slug:"/Tutorials/Components/Core/Hider",permalink:"/Tutorials/Components/Core/Hider",draft:!1,tags:[],version:"current",frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"Grids",permalink:"/Tutorials/Components/Core/Grids"},next:{title:"IfcGeometryTiler",permalink:"/Tutorials/Components/Core/IfcGeometryTiler"}},p={},m=[{value:"\ud83d\udc53 Making things invisible",id:"-making-things-invisible",level:3},{value:"\ud83c\udf0e Setting up a simple scene",id:"-setting-up-a-simple-scene",level:3},{value:"\ud83d\udd0e Custom filters for your BIM models",id:"-custom-filters-for-your-bim-models",level:3},{value:"\ud83d\udcd5\ud83d\udcd7\ud83d\udcd8 Setting up simple filters",id:"-setting-up-simple-filters",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}],u={toc:m},d="wrapper";function h(e){var t=e.components,n=(0,a.Z)(e,i);return(0,r.kt)(d,(0,o.Z)({},u,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/fragments/Hider/example.ts"},"here"),".")),(0,r.kt)("h3",{id:"-making-things-invisible"},"\ud83d\udc53 Making things invisible"),(0,r.kt)("hr",null),(0,r.kt)("p",null,"In this tutorial, you'll learn how control the visibility of the items of a BIM model."),(0,r.kt)("admonition",{title:"Why make things invisible?",type:"tip"},(0,r.kt)("p",{parentName:"admonition"},"Many times, we just want to look at a specific part of a BIM model, without seeing the rest of it. BIM models are complex, and finding what we are looking for is not always easy. Luckily, the components library has tools to make it easier!")),(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"},"web-ifc")," to get some IFC items."),(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:"-custom-filters-for-your-bim-models"},"\ud83d\udd0e Custom filters for your BIM models"),(0,r.kt)("hr",null),(0,r.kt)("p",null,"First, let's start by creating a ",(0,r.kt)("inlineCode",{parentName:"p"},"FragmentManager")," instance and\nloading a simple fragment. If you haven't checked out the tutorial\nfor that component yet, do it before going forward!"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},'const fragments = components.get(OBC.FragmentsManager);\nconst file = await fetch(\n "https://thatopen.github.io/engine_components/resources/small.frag",\n);\nconst data = await file.arrayBuffer();\nconst buffer = new Uint8Array(data);\nconst model = fragments.load(buffer);\nworld.scene.three.add(model);\n\nconst properties = await fetch(\n "https://thatopen.github.io/engine_components/resources/small.json",\n);\nmodel.setLocalProperties(await properties.json());\n\nconst indexer = components.get(OBC.IfcRelationsIndexer);\nconst relationsFile = await fetch(\n "https://thatopen.github.io/engine_components/resources/small-relations.json",\n);\nconst relations = indexer.getRelationsMapFromJSON(await relationsFile.text());\nindexer.setRelationMap(model, relations);\n')),(0,r.kt)("p",null,"Now that we have our model, let's start the ",(0,r.kt)("inlineCode",{parentName:"p"},"FragmentHider"),". You\ncan use the ",(0,r.kt)("inlineCode",{parentName:"p"},"loadCached")," method if you had used it before: it will\nautomatically load all the filters you created in previous sessions,\neven after closing the browser and opening it again:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},"const hider = components.get(OBC.Hider);\n")),(0,r.kt)("h3",{id:"-setting-up-simple-filters"},"\ud83d\udcd5\ud83d\udcd7\ud83d\udcd8 Setting up simple filters"),(0,r.kt)("hr",null),(0,r.kt)("p",null,"Next, we will classify data by category and by level using the ",(0,r.kt)("inlineCode",{parentName:"p"},"Classifier"),". This will allow us to create a simple filter for both classifications."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},"const classifier = components.get(OBC.Classifier);\nclassifier.byEntity(model);\nawait classifier.bySpatialStructure(model);\n")),(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,"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:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},"const spatialStructures: Record = {};\nconst structureNames = Object.keys(classifier.list.spatialStructures);\nfor (const name of structureNames) {\n spatialStructures[name] = true;\n}\n")),(0,r.kt)("p",null,"Now, let's do the same for categories:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},"const classes: Record = {};\nconst classNames = Object.keys(classifier.list.entities);\nfor (const name of classNames) {\n classes[name] = true;\n}\n")),(0,r.kt)("p",null,"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!"),(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});\n\ndocument.body.append(panel);\n\nconst floorSection = panel.querySelector(\n "bim-panel-section[name=\'Floors\']",\n) as BUI.PanelSection;\n\nconst categorySection = panel.querySelector(\n "bim-panel-section[name=\'Categories\']",\n) as BUI.PanelSection;\n\nfor (const name in spatialStructures) {\n const panel = BUI.Component.create(() => {\n return BUI.html`\n \n \n `;\n });\n floorSection.append(panel);\n}\n\nfor (const name in classes) {\n const checkbox = BUI.Component.create(() => {\n return BUI.html`\n \n \n `;\n });\n categorySection.append(checkbox);\n}\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! 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!"),(0,r.kt)("iframe",{src:"https://thatopen.github.io/engine_components/examples/Hider"}))}h.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/build/assets/js/fba9713b.d1723b16.js b/build/assets/js/fba9713b.d1723b16.js deleted file mode 100644 index 207cbfbfa..000000000 --- a/build/assets/js/fba9713b.d1723b16.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkdocs=self.webpackChunkdocs||[]).push([[3132],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>f});var o=n(7294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(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||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(o=0;o=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var l=o.createContext({}),c=function(e){var t=o.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},p=function(e){var t=c(e.components);return o.createElement(l.Provider,{value:t},e.children)},m="mdxType",u={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},d=o.forwardRef((function(e,t){var n=e.components,r=e.mdxType,a=e.originalType,l=e.parentName,p=s(e,["components","mdxType","originalType","parentName"]),m=c(n),d=r,f=m["".concat(l,".").concat(d)]||m[d]||u[d]||a;return n?o.createElement(f,i(i({ref:t},p),{},{components:n})):o.createElement(f,i({ref:t},p))}));function f(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var a=n.length,i=new Array(a);i[0]=d;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[m]="string"==typeof e?e:r,i[1]=s;for(var c=2;c{n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>l,default:()=>f,frontMatter:()=>s,metadata:()=>c,toc:()=>m});var o=n(7462),r=n(3366),a=(n(7294),n(3905)),i=["components"],s={},l=void 0,c={unversionedId:"Tutorials/Components/Core/Hider",id:"Tutorials/Components/Core/Hider",title:"Hider",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/Hider.mdx",sourceDirName:"Tutorials/Components/Core",slug:"/Tutorials/Components/Core/Hider",permalink:"/Tutorials/Components/Core/Hider",draft:!1,tags:[],version:"current",frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"Grids",permalink:"/Tutorials/Components/Core/Grids"},next:{title:"IfcGeometryTiler",permalink:"/Tutorials/Components/Core/IfcGeometryTiler"}},p={},m=[{value:"\ud83d\udd0e Custom filters for your BIM models",id:"-custom-filters-for-your-bim-models",level:3},{value:"\ud83d\udcd5\ud83d\udcd7\ud83d\udcd8 Setting up simple filters",id:"-setting-up-simple-filters",level:3}],u={toc:m},d="wrapper";function f(e){var t=e.components,n=(0,r.Z)(e,i);return(0,a.kt)(d,(0,o.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("admonition",{title:"Source",type:"info"},(0,a.kt)("p",{parentName:"admonition"},"Copying and pasting? We've got you covered! You can find the full source code of this tutorial ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/ThatOpen/engine_components/blob/main/packages/core/src/fragments/Hider/example.ts"},"here"),".")),(0,a.kt)("h3",{id:"-custom-filters-for-your-bim-models"},"\ud83d\udd0e Custom filters for your BIM models"),(0,a.kt)("hr",null),(0,a.kt)("p",null,"BIM models are complex, and finding what we are looking for is not\nalways easy. Luckily, the components library has tools to make\nit easier, and one of them is the 'FragmentHider'. Let's\ncheck it out!"),(0,a.kt)("admonition",{title:"Complex IFC, complex filters",type:"info"},(0,a.kt)("p",{parentName:"admonition"},"Each IFC is a world. Data is always defined slightly differently,\nand defining pre-made filters only works for very basic things\nlike categories. With the FragmentHider, you'll be able to find\nanything, even things defined in custom categories!")),(0,a.kt)("p",null,"First, let's start by creating a ",(0,a.kt)("inlineCode",{parentName:"p"},"FragmentManager")," instance and\nloading a simple fragment. If you haven't checked out the tutorial\nfor that component yet, do it before going forward!"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-js"},'const fragments = new OBC.FragmentsManager(components);\nconst file = await fetch("https://thatopen.github.io/engine_components/resources/small.frag");\nconst data = await file.arrayBuffer();\nconst buffer = new Uint8Array(data);\nconst model = fragments.load(buffer);\nworld.scene.three.add(model);\n')),(0,a.kt)("p",null,"Now that we have our model, let's start the ",(0,a.kt)("inlineCode",{parentName:"p"},"FragmentHider"),". You\ncan use the ",(0,a.kt)("inlineCode",{parentName:"p"},"loadCached")," method if you had used it before: it will\nautomatically load all the filters you created in previous sessions,\neven after closing the browser and opening it again:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-js"},"const hider = new OBC.FragmentHider(components);\n")),(0,a.kt)("h3",{id:"-setting-up-simple-filters"},"\ud83d\udcd5\ud83d\udcd7\ud83d\udcd8 Setting up simple filters"),(0,a.kt)("hr",null),(0,a.kt)("p",null,"Next, we will classify data by category and by level using the\n",(0,a.kt)("inlineCode",{parentName:"p"},"FragmentClassifier"),". This will allow us to create a simple\nfilter for both classifications. Don't worry: we'll get to\nthe more complex filters later!"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-js"},"const classifier = new OBC.Classifier(components);\nclassifier.byStorey(model);\nclassifier.byEntity(model);\n")),(0,a.kt)("p",null,"Next, we will create a simple object that we will use as the\nbase for the floors filter. It will just be a JS object with\nthe name of each storey as key and a boolean (true/false) as\nvalue:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-js"},"const storeys: Record = {};\nconst storeyNames = Object.keys(classifier.list.storeys);\nfor (const name of storeyNames) {\n storeys[name] = true;\n}\n")),(0,a.kt)("p",null,"Now, let's do the same for categories:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-js"},"const classes: Record = {};\nconst classNames = Object.keys(classifier.list.entities);\nfor (const name of classNames) {\n classes[name] = true;\n}\n")),(0,a.kt)("p",null,"Finally, we will set up a simple menu to control\nthe visibility of storeys:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-js"},'BUI.Manager.init();\n\nconst 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\nconst floorSection = panel.querySelector(\n "bim-panel-section[name=\'Floors\']",\n) as BUI.PanelSection;\n\nconst categorySection = panel.querySelector(\n "bim-panel-section[name=\'Categories\']",\n) as BUI.PanelSection;\n\nfor (const name in storeys) {\n const panel = BUI.Component.create(() => {\n return BUI.html`\n \n \n `;\n });\n floorSection.append(panel);\n}\n\nfor (const name in classes) {\n const checkbox = BUI.Component.create(() => {\n return BUI.html`\n \n \n `;\n });\n categorySection.append(checkbox);\n}\n')),(0,a.kt)("p",null,"That's it! That button will open a floating menu that will allow\nyou to create custom multi-filters that work even for custom\nproperty sets and quantity sets, including logical operators.\nTry them out in the example below, and check out more tutorials\nto bring your BIM apps to the next level!"),(0,a.kt)("iframe",{src:"https://thatopen.github.io/engine_components/examples/Hider"}))}f.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/build/assets/js/main.7e8cae8f.js b/build/assets/js/main.7e8cae8f.js deleted file mode 100644 index d3bd6a33d..000000000 --- a/build/assets/js/main.7e8cae8f.js +++ /dev/null @@ -1,2 +0,0 @@ -/*! For license information please see main.7e8cae8f.js.LICENSE.txt */ -(self.webpackChunkdocs=self.webpackChunkdocs||[]).push([[179],{723:(e,t,n)=>{"use strict";n.d(t,{Z:()=>f});var r=n(7294),a=n(7462),o=n(8356),i=n.n(o),l=n(6887);const s={"02b3ccc6":[function(){return n.e(440).then(n.bind(n,6419))},"@site/docs/api/interfaces/thatopen_components.CameraControllable.md",6419],"02cb068a":[function(){return n.e(9098).then(n.bind(n,2620))},"@site/docs/api/classes/thatopen_components_front.PostproductionRenderer.md",2620],"035e0bbb":[function(){return n.e(5269).then(n.bind(n,8923))},"@site/docs/Tutorials/Components/Core/OrthoPerspectiveCamera.mdx",8923],"070f01c0":[function(){return n.e(8092).then(n.bind(n,4736))},"@site/docs/api/classes/thatopen_components_front.Plans.md",4736],"072af5be":[function(){return n.e(9170).then(n.bind(n,1431))},"@site/docs/api/modules/thatopen_components_front.md",1431],"0849f8a0":[function(){return n.e(3659).then(n.bind(n,4586))},"@site/docs/api/classes/thatopen_components.Base.md",4586],"0d0787b6":[function(){return n.e(8509).then(n.bind(n,1831))},"@site/docs/api/interfaces/thatopen_components.Createable.md",1831],"0dcd76a4":[function(){return n.e(7116).then(n.bind(n,396))},"@site/docs/Tutorials/UserInterface/OBC/EntityAttributes.mdx",396],"0e384e19":[function(){return n.e(9671).then(n.bind(n,9881))},"@site/docs/intro.md",9881],"0f647618":[function(){return n.e(1393).then(n.bind(n,512))},"@site/docs/api/interfaces/thatopen_ui.EntryQuery.md",512],"10d63750":[function(){return n.e(8645).then(n.bind(n,178))},"@site/docs/api/modules/thatopen_ui_obc.md",178],"111feb22":[function(){return n.e(94).then(n.bind(n,817))},"@site/docs/api/classes/thatopen_ui.ColorInput.md",817],"140c5f61":[function(){return n.e(3386).then(n.bind(n,6789))},"@site/docs/Tutorials/Components/Front/AngleMeasurement.mdx",6789],"14b2fcb7":[function(){return n.e(350).then(n.bind(n,6544))},"@site/docs/api/classes/thatopen_components.PlanMode.md",6544],"1567e004":[function(){return n.e(2238).then(n.bind(n,7745))},"@site/docs/api/classes/thatopen_components.Disposer.md",7745],"15cde90e":[function(){return n.e(4321).then(n.bind(n,9003))},"@site/docs/api/classes/thatopen_ui.Component.md",9003],"16dd699a":[function(){return n.e(8141).then(n.bind(n,2075))},"@site/docs/api/interfaces/thatopen_ui.HasName.md",2075],17896441:[function(){return Promise.all([n.e(532),n.e(7918)]).then(n.bind(n,907))},"@theme/DocItem",907],"17d65022":[function(){return n.e(5243).then(n.bind(n,7429))},"@site/docs/api/classes/thatopen_ui.NumberInput.md",7429],"1b39a4e0":[function(){return n.e(2394).then(n.bind(n,3486))},"@site/docs/api/interfaces/thatopen_ui.QueryGroup.md",3486],"1be78505":[function(){return Promise.all([n.e(532),n.e(9514)]).then(n.bind(n,9963))},"@theme/DocPage",9963],"1c46db60":[function(){return n.e(1221).then(n.bind(n,259))},"@site/docs/api/classes/thatopen_ui.ContextMenu.md",259],"1c5bde81":[function(){return n.e(761).then(n.bind(n,8143))},"@site/docs/api/interfaces/thatopen_components.Hideable.md",8143],"232409b2":[function(){return n.e(5362).then(n.bind(n,1747))},"@site/docs/api/classes/thatopen_components.MeshCullerRenderer.md",1747],"23f0a572":[function(){return n.e(400).then(n.bind(n,7502))},"@site/docs/api/classes/thatopen_components.Event.md",7502],"255d81eb":[function(){return n.e(1129).then(n.bind(n,2757))},"@site/docs/api/classes/thatopen_ui.TextInput.md",2757],"26cef902":[function(){return n.e(8230).then(n.bind(n,6677))},"@site/docs/api/interfaces/thatopen_components.Resizeable.md",6677],"27876b76":[function(){return n.e(9908).then(n.bind(n,5610))},"@site/docs/api/classes/thatopen_ui_obc.ViewCube.md",5610],"2895e020":[function(){return n.e(3149).then(n.bind(n,1602))},"@site/docs/api/interfaces/thatopen_ui.ColumnData.md",1602],"2a9a2072":[function(){return n.e(1860).then(n.bind(n,9859))},"@site/docs/Tutorials/Components/Core/BoundingBoxer.mdx",9859],"2aba4d5d":[function(){return n.e(282).then(n.bind(n,2019))},"@site/docs/Tutorials/Components/Core/Cullers.mdx",2019],"308c405f":[function(){return n.e(8491).then(n.bind(n,9842))},"@site/docs/Tutorials/Components/Core/IfcRelationsIndexer.mdx",9842],"35bc646f":[function(){return n.e(6635).then(n.bind(n,22))},"@site/docs/Tutorials/Components/Core/Clipper.mdx",22],"36f30aba":[function(){return n.e(6383).then(n.bind(n,4451))},"@site/docs/api/classes/thatopen_ui.Button.md",4451],"3834acf5":[function(){return n.e(984).then(n.bind(n,6983))},"@site/docs/api/classes/thatopen_components.FirstPersonMode.md",6983],"38ed86ac":[function(){return n.e(9926).then(n.bind(n,6083))},"@site/docs/api/interfaces/thatopen_components.Updateable.md",6083],"3eac639e":[function(){return n.e(8100).then(n.bind(n,9142))},"@site/docs/Tutorials/UserInterface/OBC/ClassificationsTree.mdx",9142],"416b2475":[function(){return n.e(8470).then(n.bind(n,584))},"@site/docs/api/classes/thatopen_components.IfcStreamingSettings.md",584],"44d0ab4a":[function(){return n.e(1764).then(n.bind(n,1382))},"@site/docs/api/classes/thatopen_components.Clipper.md",1382],"4bf8fd60":[function(){return n.e(8995).then(n.bind(n,5028))},"@site/docs/api/classes/thatopen_components.BaseWorldItem.md",5028],"4d54d076":[function(){return n.e(7080).then(n.bind(n,1933))},"@site/docs/contributing.md",1933],"4f749076":[function(){return n.e(261).then(n.bind(n,8383))},"@site/docs/api/classes/thatopen_ui.PanelSection.md",8383],"5094df0e":[function(){return n.e(7621).then(n.bind(n,3263))},"@site/docs/components/tutorial-paths.md",3263],"519cde36":[function(){return n.e(6644).then(n.bind(n,3334))},"@site/docs/api/classes/thatopen_components.ProjectionManager.md",3334],"5251865f":[function(){return n.e(3156).then(n.bind(n,1449))},"@site/docs/api/interfaces/thatopen_components.NavigationMode.md",1449],"5416c1e0":[function(){return n.e(7283).then(n.bind(n,6808))},"@site/docs/api/modules/thatopen_ui.md",6808],"57f3dce9":[function(){return n.e(7027).then(n.bind(n,1207))},"@site/docs/api/classes/thatopen_fragments.Serializer.md",1207],"5a3844a3":[function(){return n.e(6162).then(n.bind(n,3121))},"@site/docs/api/classes/thatopen_components_front.Marker.md",3121],"5c7b714e":[function(){return n.e(4283).then(n.bind(n,2778))},"@site/docs/Tutorials/Components/Core/FragmentsManager.mdx",2778],"5e8c322a":[function(){return n.e(7597).then(n.bind(n,7926))},"@site/docs/api/index.md",7926],"5e9f5e1a":[function(){return Promise.resolve().then(n.bind(n,6809))},"@generated/docusaurus.config",6809],"60d286c5":[function(){return n.e(327).then(n.bind(n,2798))},"@site/docs/api/classes/thatopen_ui.Option.md",2798],"60f3e948":[function(){return n.e(1513).then(n.bind(n,6850))},"@site/docs/Tutorials/Components/Core/MiniMap.mdx",6850],"63ea579c":[function(){return n.e(8648).then(n.bind(n,2242))},"@site/docs/api/modules/thatopen_components.md",2242],"65e97039":[function(){return n.e(8094).then(n.bind(n,1328))},"@site/docs/api/classes/thatopen_ui.Icon.md",1328],"6aecd34e":[function(){return n.e(5260).then(n.bind(n,7731))},"@site/docs/api/classes/thatopen_components_front.LengthMeasurement.md",7731],"6b4facaa":[function(){return n.e(7942).then(n.bind(n,3362))},"@site/docs/api/classes/thatopen_ui.Table.md",3362],"6ff4b68b":[function(){return n.e(6015).then(n.bind(n,5825))},"@site/docs/api/classes/thatopen_components.BoundingBoxer.md",5825],"72d46929":[function(){return n.e(6811).then(n.bind(n,4774))},"@site/docs/Tutorials/Components/Core/Raycasters.mdx",4774],"733d79ef":[function(){return n.e(2720).then(n.bind(n,5666))},"@site/docs/api/classes/thatopen_components.SimpleCamera.md",5666],"73803af1":[function(){return n.e(7154).then(n.bind(n,4149))},"@site/docs/api/classes/thatopen_ui.Checkbox.md",4149],"746b65d1":[function(){return n.e(1625).then(n.bind(n,5271))},"@site/docs/api/classes/thatopen_components.PropertiesStreamingSettings.md",5271],"753b1b55":[function(){return n.e(4718).then(n.bind(n,1199))},"@site/docs/api/classes/thatopen_ui.Dropdown.md",1199],"7887f5d5":[function(){return n.e(3893).then(n.bind(n,2193))},"@site/docs/api/classes/thatopen_components_front.ClipEdges.md",2193],"7cc95c1b":[function(){return n.e(9595).then(n.bind(n,6145))},"@site/docs/Tutorials/UserInterface/OBC/ElementProperties.mdx",6145],"7d516a78":[function(){return n.e(4847).then(n.bind(n,4590))},"@site/docs/Tutorials/Components/index.md",4590],"7f370fb4":[function(){return n.e(4591).then(n.bind(n,1531))},"@site/docs/Tutorials/Components/Front/EdgesClipper.mdx",1531],"8004d10c":[function(){return n.e(8892).then(n.t.bind(n,3769,19))},"C:\\Users\\anton\\Desktop\\code\\engine_docs\\.docusaurus\\docusaurus-plugin-content-docs\\default\\plugin-route-context-module-100.json",3769],"80bb5c14":[function(){return n.e(4125).then(n.bind(n,7493))},"@site/docs/Tutorials/Components/Front/PostproductionRenderer.mdx",7493],"82c425bb":[function(){return n.e(8604).then(n.bind(n,3106))},"@site/docs/components/getting-started.md",3106],"8368d781":[function(){return n.e(8985).then(n.bind(n,5400))},"@site/docs/api/classes/thatopen_components.FragmentsManager.md",5400],"87654e65":[function(){return n.e(8655).then(n.bind(n,2640))},"@site/docs/Tutorials/Components/Core/Worlds.mdx",2640],"89d80fa0":[function(){return n.e(2837).then(n.bind(n,6737))},"@site/docs/api/classes/thatopen_ui.Toolbar.md",6737],"8e79a5c7":[function(){return n.e(638).then(n.bind(n,6095))},"@site/docs/api/interfaces/thatopen_components.BVHGeometry.md",6095],"8f0e23c6":[function(){return n.e(5282).then(n.bind(n,6860))},"@site/docs/api/classes/thatopen_ui.Selector.md",6860],"9064555a":[function(){return n.e(3738).then(n.bind(n,9889))},"@site/docs/api/classes/thatopen_components.IfcRelationsIndexer.md",9889],"935f2afb":[function(){return n.e(53).then(n.t.bind(n,1109,19))},"~docs/default/version-current-metadata-prop-751.json",1109],"9417cdd2":[function(){return n.e(2609).then(n.bind(n,7172))},"@site/docs/api/classes/thatopen_ui.Panel.md",7172],"96c1a8ad":[function(){return n.e(9018).then(n.bind(n,3864))},"@site/docs/api/interfaces/thatopen_ui.HasValue.md",3864],"98508be6":[function(){return n.e(3991).then(n.bind(n,1317))},"@site/docs/api/classes/thatopen_components.Cullers.md",1317],"9b7b7fda":[function(){return n.e(5499).then(n.bind(n,6715))},"@site/docs/api/classes/thatopen_components_front.EdgesPlane.md",6715],"9bd74671":[function(){return n.e(6061).then(n.bind(n,337))},"@site/docs/api/classes/thatopen_components.AsyncEvent.md",337],"9cc62e14":[function(){return n.e(3208).then(n.bind(n,8152))},"@site/docs/api/classes/thatopen_components.SimplePlane.md",8152],"9d7eea47":[function(){return n.e(3941).then(n.bind(n,1344))},"@site/docs/components/creating-components.md",1344],"9dd8a0d2":[function(){return n.e(7054).then(n.bind(n,7275))},"@site/src/pages/index.jsx",7275],"9f1bf482":[function(){return n.e(9311).then(n.bind(n,3564))},"@site/docs/Tutorials/UserInterface/Core/Component.mdx",3564],a19b4c2c:[function(){return n.e(372).then(n.bind(n,2970))},"@site/docs/api/classes/thatopen_components.IfcJsonExporter.md",2970],a599bc11:[function(){return n.e(835).then(n.bind(n,7852))},"@site/docs/Tutorials/Components/Core/IfcPropertiesTiler.mdx",7852],a6a8c80c:[function(){return n.e(4238).then(n.bind(n,41))},"@site/docs/api/classes/thatopen_ui.ToolbarGroup.md",41],a711233c:[function(){return n.e(8239).then(n.bind(n,5312))},"@site/docs/api/classes/thatopen_ui.ToolbarSection.md",5312],ac8bbf91:[function(){return n.e(5446).then(n.bind(n,9759))},"@site/docs/Tutorials/UserInterface/index.md",9759],b1f8f8a6:[function(){return n.e(2011).then(n.bind(n,1822))},"@site/docs/api/classes/thatopen_ui.Tabs.md",1822],b46e04f8:[function(){return n.e(8400).then(n.bind(n,9733))},"@site/docs/api/classes/thatopen_ui.Viewport.md",9733],b86baa83:[function(){return n.e(5648).then(n.bind(n,4945))},"@site/docs/api/classes/thatopen_ui.Tab.md",4945],bb95e244:[function(){return n.e(1160).then(n.bind(n,2648))},"@site/docs/api/interfaces/thatopen_components.Progress.md",2648],bbb18ae5:[function(){return n.e(9231).then(n.bind(n,4442))},"@site/docs/api/classes/thatopen_components.Components.md",4442],be95f796:[function(){return n.e(5662).then(n.bind(n,1186))},"@site/docs/api/classes/thatopen_ui.Label.md",1186],bf390c76:[function(){return n.e(5264).then(n.bind(n,8222))},"@site/docs/api/classes/thatopen_components.Component.md",8222],c1b3b982:[function(){return n.e(6812).then(n.bind(n,8045))},"@site/docs/Tutorials/Components/Front/AreaMeasurement.mdx",8045],c2bbc440:[function(){return n.e(3045).then(n.bind(n,8997))},"@site/docs/api/modules/thatopen_fragments.md",8997],c62bb28c:[function(){return n.e(5447).then(n.bind(n,9986))},"@site/docs/Tutorials/Components/Front/LengthMeasurement.mdx",9986],c6f50999:[function(){return n.e(9773).then(n.bind(n,5356))},"@site/docs/api/classes/thatopen_components.SimpleRenderer.md",5356],c9909429:[function(){return n.e(9772).then(n.bind(n,2779))},"@site/docs/api/classes/thatopen_ui_obc.World2D.md",2779],ca57013d:[function(){return n.e(4394).then(n.bind(n,1671))},"@site/docs/Tutorials/Components/Front/ShadowDropper.mdx",1671],cd5a87c7:[function(){return n.e(9065).then(n.bind(n,8542))},"@site/docs/api/classes/thatopen_components.SimpleScene.md",8542],d05ab5b9:[function(){return n.e(4646).then(n.t.bind(n,5745,19))},"C:\\Users\\anton\\Desktop\\code\\engine_docs\\.docusaurus\\docusaurus-plugin-content-pages\\default\\plugin-route-context-module-100.json",5745],d3dcb6ee:[function(){return n.e(8220).then(n.bind(n,8750))},"@site/docs/api/classes/thatopen_components.CullerRenderer.md",8750],d60cd624:[function(){return n.e(6631).then(n.bind(n,4232))},"@site/docs/api/interfaces/thatopen_components.Disposable.md",4232],d9bc4fdf:[function(){return n.e(155).then(n.bind(n,287))},"@site/docs/api/classes/thatopen_components.OrthoPerspectiveCamera.md",287],da19a474:[function(){return n.e(2154).then(n.bind(n,7765))},"@site/docs/Tutorials/Components/Front/IfcStreamer.mdx",7765],dc2a58ea:[function(){return n.e(4335).then(n.bind(n,711))},"@site/docs/Tutorials/Components/Core/Grids.mdx",711],e3702cd4:[function(){return n.e(2232).then(n.bind(n,186))},"@site/docs/components/clean-components-guide.md",186],e8a4ea3d:[function(){return n.e(7592).then(n.bind(n,5082))},"@site/docs/api/classes/thatopen_components_front.RendererWith2D.md",5082],ee29e6ad:[function(){return n.e(321).then(n.bind(n,9919))},"@site/docs/api/classes/thatopen_ui.Input.md",9919],f0d87bf4:[function(){return n.e(8553).then(n.bind(n,7673))},"@site/docs/Tutorials/Components/Core/IfcLoader.mdx",7673],f45379a9:[function(){return n.e(1842).then(n.bind(n,398))},"@site/docs/Tutorials/Components/Core/IfcGeometryTiler.mdx",398],f6aebfbf:[function(){return n.e(7306).then(n.bind(n,4440))},"@site/docs/api/modules.md",4440],f70643aa:[function(){return n.e(8046).then(n.bind(n,4726))},"@site/docs/Tutorials/UserInterface/OBC/ModelsList.mdx",4726],f71f0617:[function(){return n.e(4872).then(n.bind(n,5929))},"@site/docs/api/classes/thatopen_ui.Grid.md",5929],f77615d9:[function(){return n.e(4376).then(n.bind(n,4441))},"@site/docs/api/interfaces/thatopen_components.Configurable.md",4441],f7901ade:[function(){return n.e(7784).then(n.bind(n,165))},"@site/docs/api/classes/thatopen_ui_obc.Manager.md",165],fa23a143:[function(){return n.e(7709).then(n.bind(n,7382))},"@site/docs/api/classes/thatopen_components.OrbitMode.md",7382],fba9713b:[function(){return n.e(3132).then(n.bind(n,8559))},"@site/docs/Tutorials/Components/Core/Hider.mdx",8559]};function u(e){var t=e.error,n=e.retry,a=e.pastDelay;return t?r.createElement("div",{style:{textAlign:"center",color:"#fff",backgroundColor:"#fa383e",borderColor:"#fa383e",borderStyle:"solid",borderRadius:"0.25rem",borderWidth:"1px",boxSizing:"border-box",display:"block",padding:"1rem",flex:"0 0 50%",marginLeft:"25%",marginRight:"25%",marginTop:"5rem",maxWidth:"50%",width:"100%"}},r.createElement("p",null,String(t)),r.createElement("div",null,r.createElement("button",{type:"button",onClick:n},"Retry"))):a?r.createElement("div",{style:{display:"flex",justifyContent:"center",alignItems:"center",height:"100vh"}},r.createElement("svg",{id:"loader",style:{width:128,height:110,position:"absolute",top:"calc(100vh - 64%)"},viewBox:"0 0 45 45",xmlns:"http://www.w3.org/2000/svg",stroke:"#61dafb"},r.createElement("g",{fill:"none",fillRule:"evenodd",transform:"translate(1 1)",strokeWidth:"2"},r.createElement("circle",{cx:"22",cy:"22",r:"6",strokeOpacity:"0"},r.createElement("animate",{attributeName:"r",begin:"1.5s",dur:"3s",values:"6;22",calcMode:"linear",repeatCount:"indefinite"}),r.createElement("animate",{attributeName:"stroke-opacity",begin:"1.5s",dur:"3s",values:"1;0",calcMode:"linear",repeatCount:"indefinite"}),r.createElement("animate",{attributeName:"stroke-width",begin:"1.5s",dur:"3s",values:"2;0",calcMode:"linear",repeatCount:"indefinite"})),r.createElement("circle",{cx:"22",cy:"22",r:"6",strokeOpacity:"0"},r.createElement("animate",{attributeName:"r",begin:"3s",dur:"3s",values:"6;22",calcMode:"linear",repeatCount:"indefinite"}),r.createElement("animate",{attributeName:"stroke-opacity",begin:"3s",dur:"3s",values:"1;0",calcMode:"linear",repeatCount:"indefinite"}),r.createElement("animate",{attributeName:"stroke-width",begin:"3s",dur:"3s",values:"2;0",calcMode:"linear",repeatCount:"indefinite"})),r.createElement("circle",{cx:"22",cy:"22",r:"8"},r.createElement("animate",{attributeName:"r",begin:"0s",dur:"1.5s",values:"6;1;2;3;4;5;6",calcMode:"linear",repeatCount:"indefinite"}))))):null}var c=n(9670),p=n(226);function d(e,t){if("*"===e)return i()({loading:u,loader:function(){return n.e(4972).then(n.bind(n,4972))},modules:["@theme/NotFound"],webpack:function(){return[4972]},render:function(e,t){var n=e.default;return r.createElement(p.z,{value:{plugin:{name:"native",id:"default"}}},r.createElement(n,t))}});var o=l[e+"-"+t],d={},f=[],m=[],h=(0,c.Z)(o);return Object.entries(h).forEach((function(e){var t=e[0],n=e[1],r=s[n];r&&(d[t]=r[0],f.push(r[1]),m.push(r[2]))})),i().Map({loading:u,loader:d,modules:f,webpack:function(){return m},render:function(t,n){var i=JSON.parse(JSON.stringify(o));Object.entries(t).forEach((function(t){var n=t[0],r=t[1],a=r.default;if(!a)throw new Error("The page component at "+e+" doesn't have a default export. This makes it impossible to render anything. Consider default-exporting a React component.");"object"!=typeof a&&"function"!=typeof a||Object.keys(r).filter((function(e){return"default"!==e})).forEach((function(e){a[e]=r[e]}));var o=i,l=n.split(".");l.slice(0,-1).forEach((function(e){o=o[e]})),o[l[l.length-1]]=a}));var l=i.__comp;delete i.__comp;var s=i.__context;return delete i.__context,r.createElement(p.z,{value:s},r.createElement(l,(0,a.Z)({},i,n)))}})}const f=[{path:"/",component:d("/","cfc"),exact:!0},{path:"/",component:d("/","c43"),routes:[{path:"/api/",component:d("/api/","093"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_components_front.ClipEdges",component:d("/api/classes/thatopen_components_front.ClipEdges","4db"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_components_front.EdgesPlane",component:d("/api/classes/thatopen_components_front.EdgesPlane","d87"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_components_front.LengthMeasurement",component:d("/api/classes/thatopen_components_front.LengthMeasurement","a2d"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_components_front.Marker",component:d("/api/classes/thatopen_components_front.Marker","8a0"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_components_front.Plans",component:d("/api/classes/thatopen_components_front.Plans","0a9"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_components_front.PostproductionRenderer",component:d("/api/classes/thatopen_components_front.PostproductionRenderer","e35"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_components_front.RendererWith2D",component:d("/api/classes/thatopen_components_front.RendererWith2D","01b"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_components.AsyncEvent",component:d("/api/classes/thatopen_components.AsyncEvent","ce8"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_components.Base",component:d("/api/classes/thatopen_components.Base","c54"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_components.BaseWorldItem",component:d("/api/classes/thatopen_components.BaseWorldItem","0af"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_components.BoundingBoxer",component:d("/api/classes/thatopen_components.BoundingBoxer","98f"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_components.Clipper",component:d("/api/classes/thatopen_components.Clipper","133"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_components.Component",component:d("/api/classes/thatopen_components.Component","afe"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_components.Components",component:d("/api/classes/thatopen_components.Components","aa3"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_components.CullerRenderer",component:d("/api/classes/thatopen_components.CullerRenderer","f3b"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_components.Cullers",component:d("/api/classes/thatopen_components.Cullers","fca"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_components.Disposer",component:d("/api/classes/thatopen_components.Disposer","b5b"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_components.Event",component:d("/api/classes/thatopen_components.Event","f8d"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_components.FirstPersonMode",component:d("/api/classes/thatopen_components.FirstPersonMode","d2a"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_components.FragmentsManager",component:d("/api/classes/thatopen_components.FragmentsManager","773"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_components.IfcJsonExporter",component:d("/api/classes/thatopen_components.IfcJsonExporter","ee1"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_components.IfcRelationsIndexer",component:d("/api/classes/thatopen_components.IfcRelationsIndexer","9c5"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_components.IfcStreamingSettings",component:d("/api/classes/thatopen_components.IfcStreamingSettings","dac"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_components.MeshCullerRenderer",component:d("/api/classes/thatopen_components.MeshCullerRenderer","579"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_components.OrbitMode",component:d("/api/classes/thatopen_components.OrbitMode","eff"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_components.OrthoPerspectiveCamera",component:d("/api/classes/thatopen_components.OrthoPerspectiveCamera","083"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_components.PlanMode",component:d("/api/classes/thatopen_components.PlanMode","d2f"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_components.ProjectionManager",component:d("/api/classes/thatopen_components.ProjectionManager","beb"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_components.PropertiesStreamingSettings",component:d("/api/classes/thatopen_components.PropertiesStreamingSettings","016"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_components.SimpleCamera",component:d("/api/classes/thatopen_components.SimpleCamera","93f"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_components.SimplePlane",component:d("/api/classes/thatopen_components.SimplePlane","317"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_components.SimpleRenderer",component:d("/api/classes/thatopen_components.SimpleRenderer","dfc"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_components.SimpleScene",component:d("/api/classes/thatopen_components.SimpleScene","d61"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_fragments.Serializer",component:d("/api/classes/thatopen_fragments.Serializer","2c0"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_ui_obc.Manager",component:d("/api/classes/thatopen_ui_obc.Manager","366"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_ui_obc.ViewCube",component:d("/api/classes/thatopen_ui_obc.ViewCube","1a6"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_ui_obc.World2D",component:d("/api/classes/thatopen_ui_obc.World2D","9f6"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_ui.Button",component:d("/api/classes/thatopen_ui.Button","ae4"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_ui.Checkbox",component:d("/api/classes/thatopen_ui.Checkbox","4ac"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_ui.ColorInput",component:d("/api/classes/thatopen_ui.ColorInput","71d"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_ui.Component",component:d("/api/classes/thatopen_ui.Component","5d4"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_ui.ContextMenu",component:d("/api/classes/thatopen_ui.ContextMenu","42b"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_ui.Dropdown",component:d("/api/classes/thatopen_ui.Dropdown","144"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_ui.Grid",component:d("/api/classes/thatopen_ui.Grid","fc8"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_ui.Icon",component:d("/api/classes/thatopen_ui.Icon","01b"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_ui.Input",component:d("/api/classes/thatopen_ui.Input","4aa"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_ui.Label",component:d("/api/classes/thatopen_ui.Label","7ab"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_ui.NumberInput",component:d("/api/classes/thatopen_ui.NumberInput","2d0"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_ui.Option",component:d("/api/classes/thatopen_ui.Option","36c"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_ui.Panel",component:d("/api/classes/thatopen_ui.Panel","289"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_ui.PanelSection",component:d("/api/classes/thatopen_ui.PanelSection","c93"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_ui.Selector",component:d("/api/classes/thatopen_ui.Selector","d7d"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_ui.Tab",component:d("/api/classes/thatopen_ui.Tab","a0b"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_ui.Table",component:d("/api/classes/thatopen_ui.Table","e0e"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_ui.Tabs",component:d("/api/classes/thatopen_ui.Tabs","ae8"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_ui.TextInput",component:d("/api/classes/thatopen_ui.TextInput","b15"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_ui.Toolbar",component:d("/api/classes/thatopen_ui.Toolbar","2e6"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_ui.ToolbarGroup",component:d("/api/classes/thatopen_ui.ToolbarGroup","2b6"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_ui.ToolbarSection",component:d("/api/classes/thatopen_ui.ToolbarSection","56f"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/classes/thatopen_ui.Viewport",component:d("/api/classes/thatopen_ui.Viewport","bb0"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/interfaces/thatopen_components.BVHGeometry",component:d("/api/interfaces/thatopen_components.BVHGeometry","211"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/interfaces/thatopen_components.CameraControllable",component:d("/api/interfaces/thatopen_components.CameraControllable","59a"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/interfaces/thatopen_components.Configurable",component:d("/api/interfaces/thatopen_components.Configurable","e95"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/interfaces/thatopen_components.Createable",component:d("/api/interfaces/thatopen_components.Createable","4d2"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/interfaces/thatopen_components.Disposable",component:d("/api/interfaces/thatopen_components.Disposable","329"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/interfaces/thatopen_components.Hideable",component:d("/api/interfaces/thatopen_components.Hideable","b05"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/interfaces/thatopen_components.NavigationMode",component:d("/api/interfaces/thatopen_components.NavigationMode","5f7"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/interfaces/thatopen_components.Progress",component:d("/api/interfaces/thatopen_components.Progress","209"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/interfaces/thatopen_components.Resizeable",component:d("/api/interfaces/thatopen_components.Resizeable","432"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/interfaces/thatopen_components.Updateable",component:d("/api/interfaces/thatopen_components.Updateable","ed0"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/interfaces/thatopen_ui.ColumnData",component:d("/api/interfaces/thatopen_ui.ColumnData","f98"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/interfaces/thatopen_ui.EntryQuery",component:d("/api/interfaces/thatopen_ui.EntryQuery","fc9"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/interfaces/thatopen_ui.HasName",component:d("/api/interfaces/thatopen_ui.HasName","739"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/interfaces/thatopen_ui.HasValue",component:d("/api/interfaces/thatopen_ui.HasValue","4b9"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/interfaces/thatopen_ui.QueryGroup",component:d("/api/interfaces/thatopen_ui.QueryGroup","3b9"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/modules",component:d("/api/modules","49b"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/modules/thatopen_components",component:d("/api/modules/thatopen_components","c05"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/modules/thatopen_components_front",component:d("/api/modules/thatopen_components_front","386"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/modules/thatopen_fragments",component:d("/api/modules/thatopen_fragments","a42"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/modules/thatopen_ui",component:d("/api/modules/thatopen_ui","cfb"),exact:!0,sidebar:"tutorialSidebar"},{path:"/api/modules/thatopen_ui_obc",component:d("/api/modules/thatopen_ui_obc","14c"),exact:!0,sidebar:"tutorialSidebar"},{path:"/components/clean-components-guide",component:d("/components/clean-components-guide","181"),exact:!0,sidebar:"tutorialSidebar"},{path:"/components/creating-components",component:d("/components/creating-components","bc7"),exact:!0,sidebar:"tutorialSidebar"},{path:"/components/getting-started",component:d("/components/getting-started","8e9"),exact:!0,sidebar:"tutorialSidebar"},{path:"/components/tutorial-paths",component:d("/components/tutorial-paths","97b"),exact:!0,sidebar:"tutorialSidebar"},{path:"/contributing",component:d("/contributing","7d1"),exact:!0,sidebar:"tutorialSidebar"},{path:"/intro",component:d("/intro","283"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Tutorials/Components/",component:d("/Tutorials/Components/","e93"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Tutorials/Components/Core/BoundingBoxer",component:d("/Tutorials/Components/Core/BoundingBoxer","256"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Tutorials/Components/Core/Clipper",component:d("/Tutorials/Components/Core/Clipper","395"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Tutorials/Components/Core/Cullers",component:d("/Tutorials/Components/Core/Cullers","475"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Tutorials/Components/Core/FragmentsManager",component:d("/Tutorials/Components/Core/FragmentsManager","591"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Tutorials/Components/Core/Grids",component:d("/Tutorials/Components/Core/Grids","51a"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Tutorials/Components/Core/Hider",component:d("/Tutorials/Components/Core/Hider","5fc"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Tutorials/Components/Core/IfcGeometryTiler",component:d("/Tutorials/Components/Core/IfcGeometryTiler","d0c"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Tutorials/Components/Core/IfcLoader",component:d("/Tutorials/Components/Core/IfcLoader","2d5"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Tutorials/Components/Core/IfcPropertiesTiler",component:d("/Tutorials/Components/Core/IfcPropertiesTiler","65a"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Tutorials/Components/Core/IfcRelationsIndexer",component:d("/Tutorials/Components/Core/IfcRelationsIndexer","319"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Tutorials/Components/Core/MiniMap",component:d("/Tutorials/Components/Core/MiniMap","0f2"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Tutorials/Components/Core/OrthoPerspectiveCamera",component:d("/Tutorials/Components/Core/OrthoPerspectiveCamera","259"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Tutorials/Components/Core/Raycasters",component:d("/Tutorials/Components/Core/Raycasters","e7b"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Tutorials/Components/Core/Worlds",component:d("/Tutorials/Components/Core/Worlds","3ad"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Tutorials/Components/Front/AngleMeasurement",component:d("/Tutorials/Components/Front/AngleMeasurement","052"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Tutorials/Components/Front/AreaMeasurement",component:d("/Tutorials/Components/Front/AreaMeasurement","1ac"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Tutorials/Components/Front/EdgesClipper",component:d("/Tutorials/Components/Front/EdgesClipper","fa0"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Tutorials/Components/Front/IfcStreamer",component:d("/Tutorials/Components/Front/IfcStreamer","88f"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Tutorials/Components/Front/LengthMeasurement",component:d("/Tutorials/Components/Front/LengthMeasurement","b69"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Tutorials/Components/Front/PostproductionRenderer",component:d("/Tutorials/Components/Front/PostproductionRenderer","1a3"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Tutorials/Components/Front/ShadowDropper",component:d("/Tutorials/Components/Front/ShadowDropper","19b"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Tutorials/UserInterface/",component:d("/Tutorials/UserInterface/","a3c"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Tutorials/UserInterface/Core/Component",component:d("/Tutorials/UserInterface/Core/Component","673"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Tutorials/UserInterface/OBC/ClassificationsTree",component:d("/Tutorials/UserInterface/OBC/ClassificationsTree","e91"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Tutorials/UserInterface/OBC/ElementProperties",component:d("/Tutorials/UserInterface/OBC/ElementProperties","d6c"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Tutorials/UserInterface/OBC/EntityAttributes",component:d("/Tutorials/UserInterface/OBC/EntityAttributes","d87"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Tutorials/UserInterface/OBC/ModelsList",component:d("/Tutorials/UserInterface/OBC/ModelsList","c22"),exact:!0,sidebar:"tutorialSidebar"}]},{path:"*",component:d("*")}]},8934:(e,t,n)=>{"use strict";n.d(t,{_:()=>a,t:()=>o});var r=n(7294),a=r.createContext(!1);function o(e){var t=e.children,n=(0,r.useState)(!1),o=n[0],i=n[1];return(0,r.useEffect)((function(){i(!0)}),[]),r.createElement(a.Provider,{value:o},t)}},6562:(e,t,n)=>{"use strict";var r=n(7294),a=n(3935),o=n(3727),i=n(405),l=n(412);const s=[n(2497),n(3310),n(8320),n(2295)];var u=n(723),c=n(6550),p=n(8790);const d={gradientBackground:"gradientBackground_W6vG",accentGradient:"accentGradient_xnKm","accent-movement":"accent-movement_uLvs",mainGradient:"mainGradient_BHIB","main-movement":"main-movement_tjJ8"};function f(e){var t=e.children;return r.createElement(r.Fragment,null,r.createElement(m,null),t)}function m(){return r.createElement("div",{className:d.gradientBackground},r.createElement(h,null),r.createElement(g,null))}function h(){return r.createElement("div",{className:d.accentGradient})}function g(){return r.createElement("div",{className:d.mainGradient})}function b(e){var t=e.children;return r.createElement(f,null,t)}var v=n(7462),y=n(5742),w=n(2263),_=n(4996),S=n(6668),k=n(1944),E=n(4711),C=n(9727),x=n(3320),T=n(8780),P=n(197);function A(){var e=(0,w.Z)().i18n,t=e.defaultLocale,n=e.localeConfigs,a=(0,E.l)();return r.createElement(y.Z,null,Object.entries(n).map((function(e){var t=e[0],n=e[1].htmlLang;return r.createElement("link",{key:t,rel:"alternate",href:a.createUrl({locale:t,fullyQualified:!0}),hrefLang:n})})),r.createElement("link",{rel:"alternate",href:a.createUrl({locale:t,fullyQualified:!0}),hrefLang:"x-default"}))}function L(e){var t=e.permalink,n=(0,w.Z)().siteConfig.url,a=function(){var e=(0,w.Z)().siteConfig,t=e.url,n=e.baseUrl,r=e.trailingSlash,a=(0,c.TH)().pathname;return t+(0,T.applyTrailingSlash)((0,_.Z)(a),{trailingSlash:r,baseUrl:n})}(),o=t?""+n+t:a;return r.createElement(y.Z,null,r.createElement("meta",{property:"og:url",content:o}),r.createElement("link",{rel:"canonical",href:o}))}function R(){var e=(0,w.Z)().i18n.currentLocale,t=(0,S.L)(),n=t.metadata,a=t.image;return r.createElement(r.Fragment,null,r.createElement(y.Z,null,r.createElement("meta",{name:"twitter:card",content:"summary_large_image"}),r.createElement("body",{className:C.h})),a&&r.createElement(k.d,{image:a}),r.createElement(L,null),r.createElement(A,null),r.createElement(P.Z,{tag:x.HX,locale:e}),r.createElement(y.Z,null,n.map((function(e,t){return r.createElement("meta",(0,v.Z)({key:t},e))}))))}var O=new Map;function I(e){if(O.has(e.pathname))return Object.assign({},e,{pathname:O.get(e.pathname)});if((0,p.f)(u.Z,e.pathname).some((function(e){return!0===e.route.exact})))return O.set(e.pathname,e.pathname),e;var t=e.pathname.trim().replace(/(?:\/index)?\.html$/,"")||"/";return O.set(e.pathname,t),Object.assign({},e,{pathname:t})}var N=n(8934),M=n(8940),D=n(4578);function F(e){for(var t=arguments.length,n=new Array(t>1?t-1:0),r=1;r\n

Your Docusaurus site did not load properly.

\n

A very common reason is a wrong site baseUrl configuration.

\n

Current configured baseUrl = '+e+" "+("/"===e?" (default value)":"")+'

\n

We suggest trying baseUrl =

\n\n'}(e)).replace(/{"use strict";n.d(t,{_:()=>c,M:()=>p});var r=n(7294),a=n(6809);const o=JSON.parse('{"docusaurus-plugin-content-docs":{"default":{"path":"/","versions":[{"name":"current","label":"Next","isLast":true,"path":"/","mainDocId":"intro","docs":[{"id":"api/classes/thatopen_components_front.ClipEdges","path":"/api/classes/thatopen_components_front.ClipEdges","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_components_front.EdgesPlane","path":"/api/classes/thatopen_components_front.EdgesPlane","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_components_front.LengthMeasurement","path":"/api/classes/thatopen_components_front.LengthMeasurement","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_components_front.Marker","path":"/api/classes/thatopen_components_front.Marker","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_components_front.Plans","path":"/api/classes/thatopen_components_front.Plans","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_components_front.PostproductionRenderer","path":"/api/classes/thatopen_components_front.PostproductionRenderer","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_components_front.RendererWith2D","path":"/api/classes/thatopen_components_front.RendererWith2D","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_components.AsyncEvent","path":"/api/classes/thatopen_components.AsyncEvent","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_components.Base","path":"/api/classes/thatopen_components.Base","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_components.BaseWorldItem","path":"/api/classes/thatopen_components.BaseWorldItem","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_components.BoundingBoxer","path":"/api/classes/thatopen_components.BoundingBoxer","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_components.Clipper","path":"/api/classes/thatopen_components.Clipper","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_components.Component","path":"/api/classes/thatopen_components.Component","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_components.Components","path":"/api/classes/thatopen_components.Components","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_components.CullerRenderer","path":"/api/classes/thatopen_components.CullerRenderer","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_components.Cullers","path":"/api/classes/thatopen_components.Cullers","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_components.Disposer","path":"/api/classes/thatopen_components.Disposer","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_components.Event","path":"/api/classes/thatopen_components.Event","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_components.FirstPersonMode","path":"/api/classes/thatopen_components.FirstPersonMode","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_components.FragmentsManager","path":"/api/classes/thatopen_components.FragmentsManager","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_components.IfcJsonExporter","path":"/api/classes/thatopen_components.IfcJsonExporter","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_components.IfcRelationsIndexer","path":"/api/classes/thatopen_components.IfcRelationsIndexer","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_components.IfcStreamingSettings","path":"/api/classes/thatopen_components.IfcStreamingSettings","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_components.MeshCullerRenderer","path":"/api/classes/thatopen_components.MeshCullerRenderer","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_components.OrbitMode","path":"/api/classes/thatopen_components.OrbitMode","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_components.OrthoPerspectiveCamera","path":"/api/classes/thatopen_components.OrthoPerspectiveCamera","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_components.PlanMode","path":"/api/classes/thatopen_components.PlanMode","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_components.ProjectionManager","path":"/api/classes/thatopen_components.ProjectionManager","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_components.PropertiesStreamingSettings","path":"/api/classes/thatopen_components.PropertiesStreamingSettings","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_components.SimpleCamera","path":"/api/classes/thatopen_components.SimpleCamera","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_components.SimplePlane","path":"/api/classes/thatopen_components.SimplePlane","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_components.SimpleRenderer","path":"/api/classes/thatopen_components.SimpleRenderer","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_components.SimpleScene","path":"/api/classes/thatopen_components.SimpleScene","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_fragments.Serializer","path":"/api/classes/thatopen_fragments.Serializer","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_ui_obc.Manager","path":"/api/classes/thatopen_ui_obc.Manager","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_ui_obc.ViewCube","path":"/api/classes/thatopen_ui_obc.ViewCube","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_ui_obc.World2D","path":"/api/classes/thatopen_ui_obc.World2D","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_ui.Button","path":"/api/classes/thatopen_ui.Button","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_ui.Checkbox","path":"/api/classes/thatopen_ui.Checkbox","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_ui.ColorInput","path":"/api/classes/thatopen_ui.ColorInput","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_ui.Component","path":"/api/classes/thatopen_ui.Component","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_ui.ContextMenu","path":"/api/classes/thatopen_ui.ContextMenu","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_ui.Dropdown","path":"/api/classes/thatopen_ui.Dropdown","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_ui.Grid","path":"/api/classes/thatopen_ui.Grid","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_ui.Icon","path":"/api/classes/thatopen_ui.Icon","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_ui.Input","path":"/api/classes/thatopen_ui.Input","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_ui.Label","path":"/api/classes/thatopen_ui.Label","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_ui.NumberInput","path":"/api/classes/thatopen_ui.NumberInput","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_ui.Option","path":"/api/classes/thatopen_ui.Option","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_ui.Panel","path":"/api/classes/thatopen_ui.Panel","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_ui.PanelSection","path":"/api/classes/thatopen_ui.PanelSection","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_ui.Selector","path":"/api/classes/thatopen_ui.Selector","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_ui.Tab","path":"/api/classes/thatopen_ui.Tab","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_ui.Table","path":"/api/classes/thatopen_ui.Table","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_ui.Tabs","path":"/api/classes/thatopen_ui.Tabs","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_ui.TextInput","path":"/api/classes/thatopen_ui.TextInput","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_ui.Toolbar","path":"/api/classes/thatopen_ui.Toolbar","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_ui.ToolbarGroup","path":"/api/classes/thatopen_ui.ToolbarGroup","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_ui.ToolbarSection","path":"/api/classes/thatopen_ui.ToolbarSection","sidebar":"tutorialSidebar"},{"id":"api/classes/thatopen_ui.Viewport","path":"/api/classes/thatopen_ui.Viewport","sidebar":"tutorialSidebar"},{"id":"api/index","path":"/api/","sidebar":"tutorialSidebar"},{"id":"api/interfaces/thatopen_components.BVHGeometry","path":"/api/interfaces/thatopen_components.BVHGeometry","sidebar":"tutorialSidebar"},{"id":"api/interfaces/thatopen_components.CameraControllable","path":"/api/interfaces/thatopen_components.CameraControllable","sidebar":"tutorialSidebar"},{"id":"api/interfaces/thatopen_components.Configurable","path":"/api/interfaces/thatopen_components.Configurable","sidebar":"tutorialSidebar"},{"id":"api/interfaces/thatopen_components.Createable","path":"/api/interfaces/thatopen_components.Createable","sidebar":"tutorialSidebar"},{"id":"api/interfaces/thatopen_components.Disposable","path":"/api/interfaces/thatopen_components.Disposable","sidebar":"tutorialSidebar"},{"id":"api/interfaces/thatopen_components.Hideable","path":"/api/interfaces/thatopen_components.Hideable","sidebar":"tutorialSidebar"},{"id":"api/interfaces/thatopen_components.NavigationMode","path":"/api/interfaces/thatopen_components.NavigationMode","sidebar":"tutorialSidebar"},{"id":"api/interfaces/thatopen_components.Progress","path":"/api/interfaces/thatopen_components.Progress","sidebar":"tutorialSidebar"},{"id":"api/interfaces/thatopen_components.Resizeable","path":"/api/interfaces/thatopen_components.Resizeable","sidebar":"tutorialSidebar"},{"id":"api/interfaces/thatopen_components.Updateable","path":"/api/interfaces/thatopen_components.Updateable","sidebar":"tutorialSidebar"},{"id":"api/interfaces/thatopen_ui.ColumnData","path":"/api/interfaces/thatopen_ui.ColumnData","sidebar":"tutorialSidebar"},{"id":"api/interfaces/thatopen_ui.EntryQuery","path":"/api/interfaces/thatopen_ui.EntryQuery","sidebar":"tutorialSidebar"},{"id":"api/interfaces/thatopen_ui.HasName","path":"/api/interfaces/thatopen_ui.HasName","sidebar":"tutorialSidebar"},{"id":"api/interfaces/thatopen_ui.HasValue","path":"/api/interfaces/thatopen_ui.HasValue","sidebar":"tutorialSidebar"},{"id":"api/interfaces/thatopen_ui.QueryGroup","path":"/api/interfaces/thatopen_ui.QueryGroup","sidebar":"tutorialSidebar"},{"id":"api/modules","path":"/api/modules","sidebar":"tutorialSidebar"},{"id":"api/modules/thatopen_components","path":"/api/modules/thatopen_components","sidebar":"tutorialSidebar"},{"id":"api/modules/thatopen_components_front","path":"/api/modules/thatopen_components_front","sidebar":"tutorialSidebar"},{"id":"api/modules/thatopen_fragments","path":"/api/modules/thatopen_fragments","sidebar":"tutorialSidebar"},{"id":"api/modules/thatopen_ui","path":"/api/modules/thatopen_ui","sidebar":"tutorialSidebar"},{"id":"api/modules/thatopen_ui_obc","path":"/api/modules/thatopen_ui_obc","sidebar":"tutorialSidebar"},{"id":"components/clean-components-guide","path":"/components/clean-components-guide","sidebar":"tutorialSidebar"},{"id":"components/creating-components","path":"/components/creating-components","sidebar":"tutorialSidebar"},{"id":"components/getting-started","path":"/components/getting-started","sidebar":"tutorialSidebar"},{"id":"components/tutorial-paths","path":"/components/tutorial-paths","sidebar":"tutorialSidebar"},{"id":"contributing","path":"/contributing","sidebar":"tutorialSidebar"},{"id":"intro","path":"/intro","sidebar":"tutorialSidebar"},{"id":"Tutorials/Components/Core/BoundingBoxer","path":"/Tutorials/Components/Core/BoundingBoxer","sidebar":"tutorialSidebar"},{"id":"Tutorials/Components/Core/Clipper","path":"/Tutorials/Components/Core/Clipper","sidebar":"tutorialSidebar"},{"id":"Tutorials/Components/Core/Cullers","path":"/Tutorials/Components/Core/Cullers","sidebar":"tutorialSidebar"},{"id":"Tutorials/Components/Core/FragmentsManager","path":"/Tutorials/Components/Core/FragmentsManager","sidebar":"tutorialSidebar"},{"id":"Tutorials/Components/Core/Grids","path":"/Tutorials/Components/Core/Grids","sidebar":"tutorialSidebar"},{"id":"Tutorials/Components/Core/Hider","path":"/Tutorials/Components/Core/Hider","sidebar":"tutorialSidebar"},{"id":"Tutorials/Components/Core/IfcGeometryTiler","path":"/Tutorials/Components/Core/IfcGeometryTiler","sidebar":"tutorialSidebar"},{"id":"Tutorials/Components/Core/IfcLoader","path":"/Tutorials/Components/Core/IfcLoader","sidebar":"tutorialSidebar"},{"id":"Tutorials/Components/Core/IfcPropertiesTiler","path":"/Tutorials/Components/Core/IfcPropertiesTiler","sidebar":"tutorialSidebar"},{"id":"Tutorials/Components/Core/IfcRelationsIndexer","path":"/Tutorials/Components/Core/IfcRelationsIndexer","sidebar":"tutorialSidebar"},{"id":"Tutorials/Components/Core/MiniMap","path":"/Tutorials/Components/Core/MiniMap","sidebar":"tutorialSidebar"},{"id":"Tutorials/Components/Core/OrthoPerspectiveCamera","path":"/Tutorials/Components/Core/OrthoPerspectiveCamera","sidebar":"tutorialSidebar"},{"id":"Tutorials/Components/Core/Raycasters","path":"/Tutorials/Components/Core/Raycasters","sidebar":"tutorialSidebar"},{"id":"Tutorials/Components/Core/Worlds","path":"/Tutorials/Components/Core/Worlds","sidebar":"tutorialSidebar"},{"id":"Tutorials/Components/Front/AngleMeasurement","path":"/Tutorials/Components/Front/AngleMeasurement","sidebar":"tutorialSidebar"},{"id":"Tutorials/Components/Front/AreaMeasurement","path":"/Tutorials/Components/Front/AreaMeasurement","sidebar":"tutorialSidebar"},{"id":"Tutorials/Components/Front/EdgesClipper","path":"/Tutorials/Components/Front/EdgesClipper","sidebar":"tutorialSidebar"},{"id":"Tutorials/Components/Front/IfcStreamer","path":"/Tutorials/Components/Front/IfcStreamer","sidebar":"tutorialSidebar"},{"id":"Tutorials/Components/Front/LengthMeasurement","path":"/Tutorials/Components/Front/LengthMeasurement","sidebar":"tutorialSidebar"},{"id":"Tutorials/Components/Front/PostproductionRenderer","path":"/Tutorials/Components/Front/PostproductionRenderer","sidebar":"tutorialSidebar"},{"id":"Tutorials/Components/Front/ShadowDropper","path":"/Tutorials/Components/Front/ShadowDropper","sidebar":"tutorialSidebar"},{"id":"Tutorials/Components/index","path":"/Tutorials/Components/","sidebar":"tutorialSidebar"},{"id":"Tutorials/UserInterface/Core/Component","path":"/Tutorials/UserInterface/Core/Component","sidebar":"tutorialSidebar"},{"id":"Tutorials/UserInterface/index","path":"/Tutorials/UserInterface/","sidebar":"tutorialSidebar"},{"id":"Tutorials/UserInterface/OBC/ClassificationsTree","path":"/Tutorials/UserInterface/OBC/ClassificationsTree","sidebar":"tutorialSidebar"},{"id":"Tutorials/UserInterface/OBC/ElementProperties","path":"/Tutorials/UserInterface/OBC/ElementProperties","sidebar":"tutorialSidebar"},{"id":"Tutorials/UserInterface/OBC/EntityAttributes","path":"/Tutorials/UserInterface/OBC/EntityAttributes","sidebar":"tutorialSidebar"},{"id":"Tutorials/UserInterface/OBC/ModelsList","path":"/Tutorials/UserInterface/OBC/ModelsList","sidebar":"tutorialSidebar"}],"draftIds":[],"sidebars":{"tutorialSidebar":{"link":{"path":"/intro","label":"intro"}}}}],"breadcrumbs":true}}}'),i=JSON.parse('{"defaultLocale":"en","locales":["en"],"path":"i18n","currentLocale":"en","localeConfigs":{"en":{"label":"English","direction":"ltr","htmlLang":"en","calendar":"gregory","path":"en"}}}');var l=n(7529);const s=JSON.parse('{"docusaurusVersion":"2.4.3","siteVersion":"0.0.0","pluginVersions":{"docusaurus-plugin-content-docs":{"type":"package","name":"@docusaurus/plugin-content-docs","version":"2.4.3"},"docusaurus-plugin-content-pages":{"type":"package","name":"@docusaurus/plugin-content-pages","version":"2.4.3"},"docusaurus-plugin-sitemap":{"type":"package","name":"@docusaurus/plugin-sitemap","version":"2.4.3"},"docusaurus-theme-classic":{"type":"package","name":"@docusaurus/theme-classic","version":"2.4.3"},"docusaurus-plugin-typedoc":{"type":"package","name":"docusaurus-plugin-typedoc","version":"0.20.2"}}}');var u={siteConfig:a.default,siteMetadata:s,globalData:o,i18n:i,codeTranslations:l},c=r.createContext(u);function p(e){var t=e.children;return r.createElement(c.Provider,{value:u},t)}},4763:(e,t,n)=>{"use strict";n.d(t,{Z:()=>f});var r=n(4578),a=n(7294),o=n(412),i=n(5742),l=n(8780),s=n(7961);function u(e){var t=e.error,n=e.tryAgain;return a.createElement("div",{style:{display:"flex",flexDirection:"column",justifyContent:"center",alignItems:"flex-start",minHeight:"100vh",width:"100%",maxWidth:"80ch",fontSize:"20px",margin:"0 auto",padding:"1rem"}},a.createElement("h1",{style:{fontSize:"3rem"}},"This page crashed"),a.createElement("button",{type:"button",onClick:n,style:{margin:"1rem 0",fontSize:"2rem",cursor:"pointer",borderRadius:20,padding:"1rem"}},"Try again"),a.createElement(c,{error:t}))}function c(e){var t=e.error,n=(0,l.getErrorCausalChain)(t).map((function(e){return e.message})).join("\n\nCause:\n");return a.createElement("p",{style:{whiteSpace:"pre-wrap"}},n)}function p(e){var t=e.error,n=e.tryAgain;return a.createElement(f,{fallback:function(){return a.createElement(u,{error:t,tryAgain:n})}},a.createElement(i.Z,null,a.createElement("title",null,"Page Error")),a.createElement(s.Z,null,a.createElement(u,{error:t,tryAgain:n})))}var d=function(e){return a.createElement(p,e)},f=function(e){function t(t){var n;return(n=e.call(this,t)||this).state={error:null},n}(0,r.Z)(t,e);var n=t.prototype;return n.componentDidCatch=function(e){o.Z.canUseDOM&&this.setState({error:e})},n.render=function(){var e=this,t=this.props.children,n=this.state.error;if(n){var r,a={error:n,tryAgain:function(){return e.setState({error:null})}};return(null!=(r=this.props.fallback)?r:d)(a)}return null!=t?t:null},t}(a.Component)},412:(e,t,n)=>{"use strict";n.d(t,{Z:()=>a});var r="undefined"!=typeof window&&"document"in window&&"createElement"in window.document;const a={canUseDOM:r,canUseEventListeners:r&&("addEventListener"in window||"attachEvent"in window),canUseIntersectionObserver:r&&"IntersectionObserver"in window,canUseViewport:r&&"screen"in window}},5742:(e,t,n)=>{"use strict";n.d(t,{Z:()=>o});var r=n(7294),a=n(405);function o(e){return r.createElement(a.ql,e)}},9960:(e,t,n)=>{"use strict";n.d(t,{Z:()=>h});var r=n(7462),a=n(3366),o=n(7294),i=n(3727),l=n(8780),s=n(2263),u=n(3919),c=n(412),p=o.createContext({collectLink:function(){}});var d=n(4996),f=["isNavLink","to","href","activeClassName","isActive","data-noBrokenLinkCheck","autoAddBaseUrl"];function m(e,t){var n,m,h=e.isNavLink,g=e.to,b=e.href,v=e.activeClassName,y=e.isActive,w=e["data-noBrokenLinkCheck"],_=e.autoAddBaseUrl,S=void 0===_||_,k=(0,a.Z)(e,f),E=(0,s.Z)().siteConfig,C=E.trailingSlash,x=E.baseUrl,T=(0,d.C)().withBaseUrl,P=(0,o.useContext)(p),A=(0,o.useRef)(null);(0,o.useImperativeHandle)(t,(function(){return A.current}));var L=g||b;var R,O=(0,u.Z)(L),I=null==L?void 0:L.replace("pathname://",""),N=void 0!==I?(R=I,S&&function(e){return e.startsWith("/")}(R)?T(R):R):void 0;N&&O&&(N=(0,l.applyTrailingSlash)(N,{trailingSlash:C,baseUrl:x}));var M=(0,o.useRef)(!1),D=h?i.OL:i.rU,F=c.Z.canUseIntersectionObserver,B=(0,o.useRef)(),j=function(){M.current||null==N||(window.docusaurus.preload(N),M.current=!0)};(0,o.useEffect)((function(){return!F&&O&&null!=N&&window.docusaurus.prefetch(N),function(){F&&B.current&&B.current.disconnect()}}),[B,N,F,O]);var U=null!=(n=null==(m=N)?void 0:m.startsWith("#"))&&n,z=!N||!O||U;return z||w||P.collectLink(N),z?o.createElement("a",(0,r.Z)({ref:A,href:N},L&&!O&&{target:"_blank",rel:"noopener noreferrer"},k)):o.createElement(D,(0,r.Z)({},k,{onMouseEnter:j,onTouchStart:j,innerRef:function(e){A.current=e,F&&e&&O&&(B.current=new window.IntersectionObserver((function(t){t.forEach((function(t){e===t.target&&(t.isIntersecting||t.intersectionRatio>0)&&(B.current.unobserve(e),B.current.disconnect(),null!=N&&window.docusaurus.prefetch(N))}))})),B.current.observe(e))},to:N},h&&{isActive:y,activeClassName:v}))}const h=o.forwardRef(m)},1875:(e,t,n)=>{"use strict";n.d(t,{Z:()=>r});const r=function(){return null}},5999:(e,t,n)=>{"use strict";n.d(t,{Z:()=>s,I:()=>l});var r=n(7294);function a(e,t){var n=e.split(/(\{\w+\})/).map((function(e,n){if(n%2==1){var r=null==t?void 0:t[e.slice(1,-1)];if(void 0!==r)return r}return e}));return n.some((function(e){return(0,r.isValidElement)(e)}))?n.map((function(e,t){return(0,r.isValidElement)(e)?r.cloneElement(e,{key:t}):e})).filter((function(e){return""!==e})):n.join("")}var o=n(7529);function i(e){var t,n,r=e.id,a=e.message;if(void 0===r&&void 0===a)throw new Error("Docusaurus translation declarations must have at least a translation id or a default translation message");return null!=(t=null!=(n=o[null!=r?r:a])?n:a)?t:r}function l(e,t){return a(i({message:e.message,id:e.id}),t)}function s(e){var t=e.children,n=e.id,o=e.values;if(t&&"string"!=typeof t)throw console.warn("Illegal children",t),new Error("The Docusaurus component only accept simple string values");var l=i({message:t,id:n});return r.createElement(r.Fragment,null,a(l,o))}},9935:(e,t,n)=>{"use strict";n.d(t,{m:()=>r});var r="default"},3919:(e,t,n)=>{"use strict";function r(e){return/^(?:\w*:|\/\/)/.test(e)}function a(e){return void 0!==e&&!r(e)}n.d(t,{Z:()=>a,b:()=>r})},4996:(e,t,n)=>{"use strict";n.d(t,{C:()=>i,Z:()=>l});var r=n(7294),a=n(2263),o=n(3919);function i(){var e=(0,a.Z)().siteConfig,t=e.baseUrl,n=e.url,i=(0,r.useCallback)((function(e,r){return function(e,t,n,r){var a=void 0===r?{}:r,i=a.forcePrependBaseUrl,l=void 0!==i&&i,s=a.absolute,u=void 0!==s&&s;if(!n||n.startsWith("#")||(0,o.b)(n))return n;if(l)return t+n.replace(/^\//,"");if(n===t.replace(/\/$/,""))return t;var c=n.startsWith(t)?n:t+n.replace(/^\//,"");return u?e+c:c}(n,t,e,r)}),[n,t]);return{withBaseUrl:i}}function l(e,t){return void 0===t&&(t={}),(0,i().withBaseUrl)(e,t)}},2263:(e,t,n)=>{"use strict";n.d(t,{Z:()=>o});var r=n(7294),a=n(8940);function o(){return(0,r.useContext)(a._)}},2389:(e,t,n)=>{"use strict";n.d(t,{Z:()=>o});var r=n(7294),a=n(8934);function o(){return(0,r.useContext)(a._)}},9670:(e,t,n)=>{"use strict";n.d(t,{Z:()=>a});var r=function(e){return"object"==typeof e&&!!e&&Object.keys(e).length>0};function a(e){var t={};return function e(n,a){Object.entries(n).forEach((function(n){var o=n[0],i=n[1],l=a?a+"."+o:o;r(i)?e(i,l):t[l]=i}))}(e),t}},226:(e,t,n)=>{"use strict";n.d(t,{_:()=>a,z:()=>o});var r=n(7294),a=r.createContext(null);function o(e){var t=e.children,n=e.value,o=r.useContext(a),i=(0,r.useMemo)((function(){return function(e){var t=e.parent,n=e.value;if(!t){if(!n)throw new Error("Unexpected: no Docusaurus route context found");if(!("plugin"in n))throw new Error("Unexpected: Docusaurus topmost route context has no `plugin` attribute");return n}var r=Object.assign({},t.data,null==n?void 0:n.data);return{plugin:t.plugin,data:r}}({parent:o,value:n})}),[o,n]);return r.createElement(a.Provider,{value:i},t)}},143:(e,t,n)=>{"use strict";n.d(t,{Iw:()=>h,gA:()=>d,_r:()=>c,Jo:()=>g,zh:()=>p,yW:()=>m,gB:()=>f});var r=n(6550),a=n(2263),o=n(9935);function i(e,t){void 0===t&&(t={});var n=(0,a.Z)().globalData[e];if(!n&&t.failfast)throw new Error('Docusaurus plugin global data not found for "'+e+'" plugin.');return n}var l=function(e){return e.versions.find((function(e){return e.isLast}))};function s(e,t){var n,a,o=function(e,t){var n=l(e);return[].concat(e.versions.filter((function(e){return e!==n})),[n]).find((function(e){return!!(0,r.LX)(t,{path:e.path,exact:!1,strict:!1})}))}(e,t),i=null==o?void 0:o.docs.find((function(e){return!!(0,r.LX)(t,{path:e.path,exact:!0,strict:!1})}));return{activeVersion:o,activeDoc:i,alternateDocVersions:i?(n=i.id,a={},e.versions.forEach((function(e){e.docs.forEach((function(t){t.id===n&&(a[e.name]=t)}))})),a):{}}}var u={},c=function(){var e;return null!=(e=i("docusaurus-plugin-content-docs"))?e:u},p=function(e){return function(e,t,n){void 0===t&&(t=o.m),void 0===n&&(n={});var r=i(e),a=null==r?void 0:r[t];if(!a&&n.failfast)throw new Error('Docusaurus plugin global data not found for "'+e+'" plugin with id "'+t+'".');return a}("docusaurus-plugin-content-docs",e,{failfast:!0})};function d(e){return void 0===e&&(e={}),function(e,t,n){void 0===n&&(n={});var a=Object.entries(e).sort((function(e,t){return t[1].path.localeCompare(e[1].path)})).find((function(e){var n=e[1];return!!(0,r.LX)(t,{path:n.path,exact:!1,strict:!1})})),o=a?{pluginId:a[0],pluginData:a[1]}:void 0;if(!o&&n.failfast)throw new Error("Can't find active docs plugin for \""+t+'" pathname, while it was expected to be found. Maybe you tried to use a docs feature that can only be used on a docs-related page? Existing docs plugin paths are: '+Object.values(e).map((function(e){return e.path})).join(", "));return o}(c(),(0,r.TH)().pathname,e)}function f(e){return p(e).versions}function m(e){var t=p(e);return l(t)}function h(e){return s(p(e),(0,r.TH)().pathname)}function g(e){return function(e,t){var n=l(e);return{latestDocSuggestion:s(e,t).alternateDocVersions[n.name],latestVersionSuggestion:n}}(p(e),(0,r.TH)().pathname)}},8320:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(4865),a=n.n(r);a().configure({showSpinner:!1});const o={onRouteUpdate:function(e){var t=e.location,n=e.previousLocation;if(n&&t.pathname!==n.pathname){var r=window.setTimeout((function(){a().start()}),200);return function(){return window.clearTimeout(r)}}},onRouteDidUpdate:function(){a().done()}}},3310:(e,t,n)=>{"use strict";n.r(t);var r,a,o=n(7410),i=n(6809);r=o.Z,a=i.default.themeConfig.prism.additionalLanguages,globalThis.Prism=r,a.forEach((function(e){n(6726)("./prism-"+e)})),delete globalThis.Prism},9471:(e,t,n)=>{"use strict";n.d(t,{Z:()=>o});var r=n(7294);const a={iconExternalLink:"iconExternalLink_nPIU"};function o(e){var t=e.width,n=void 0===t?13.5:t,o=e.height,i=void 0===o?13.5:o;return r.createElement("svg",{width:n,height:i,"aria-hidden":"true",viewBox:"0 0 24 24",className:a.iconExternalLink},r.createElement("path",{fill:"currentColor",d:"M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"}))}},7961:(e,t,n)=>{"use strict";n.d(t,{Z:()=>Lt});var r=n(7294),a=n(6010),o=n(4763),i=n(1944),l=n(7462),s=n(6550),u=n(5999),c=n(5936),p="__docusaurus_skipToContent_fallback";function d(e){e.setAttribute("tabindex","-1"),e.focus(),e.removeAttribute("tabindex")}function f(){var e=(0,r.useRef)(null),t=(0,s.k6)().action,n=(0,r.useCallback)((function(e){e.preventDefault();var t,n=null!=(t=document.querySelector("main:first-of-type"))?t:document.getElementById(p);n&&d(n)}),[]);return(0,c.S)((function(n){var r=n.location;e.current&&!r.hash&&"PUSH"===t&&d(e.current)})),{containerRef:e,onClick:n}}var m=(0,u.I)({id:"theme.common.skipToMainContent",description:"The skip to content label used for accessibility, allowing to rapidly navigate to main content with keyboard tab/enter navigation",message:"Skip to main content"});function h(e){var t,n=null!=(t=e.children)?t:m,a=f(),o=a.containerRef,i=a.onClick;return r.createElement("div",{ref:o,role:"region","aria-label":m},r.createElement("a",(0,l.Z)({},e,{href:"#"+p,onClick:i}),n))}var g=n(5281),b=n(9727);const v={skipToContent:"skipToContent_fXgn"};function y(){return r.createElement(h,{className:v.skipToContent})}var w=n(6668),_=n(9689),S=n(3366),k=["width","height","color","strokeWidth","className"];function E(e){var t=e.width,n=void 0===t?21:t,a=e.height,o=void 0===a?21:a,i=e.color,s=void 0===i?"currentColor":i,u=e.strokeWidth,c=void 0===u?1.2:u,p=(e.className,(0,S.Z)(e,k));return r.createElement("svg",(0,l.Z)({viewBox:"0 0 15 15",width:n,height:o},p),r.createElement("g",{stroke:s,strokeWidth:c},r.createElement("path",{d:"M.75.75l13.5 13.5M14.25.75L.75 14.25"})))}const C={closeButton:"closeButton_CVFx"};function x(e){return r.createElement("button",(0,l.Z)({type:"button","aria-label":(0,u.I)({id:"theme.AnnouncementBar.closeButtonAriaLabel",message:"Close",description:"The ARIA label for close button of announcement bar"})},e,{className:(0,a.Z)("clean-btn close",C.closeButton,e.className)}),r.createElement(E,{width:14,height:14,strokeWidth:3.1}))}const T={content:"content_knG7"};function P(e){var t=(0,w.L)().announcementBar.content;return r.createElement("div",(0,l.Z)({},e,{className:(0,a.Z)(T.content,e.className),dangerouslySetInnerHTML:{__html:t}}))}const A={announcementBar:"announcementBar_mb4j",announcementBarPlaceholder:"announcementBarPlaceholder_vyr4",announcementBarClose:"announcementBarClose_gvF7",announcementBarContent:"announcementBarContent_xLdY"};function L(){var e=(0,w.L)().announcementBar,t=(0,_.nT)(),n=t.isActive,a=t.close;if(!n)return null;var o=e.backgroundColor,i=e.textColor,l=e.isCloseable;return r.createElement("div",{className:A.announcementBar,style:{backgroundColor:o,color:i},role:"banner"},l&&r.createElement("div",{className:A.announcementBarPlaceholder}),r.createElement(P,{className:A.announcementBarContent}),l&&r.createElement(x,{onClick:a,className:A.announcementBarClose}))}var R=n(2961),O=n(2466);var I=n(9688),N=n(3102),M=r.createContext(null);function D(e){var t,n,a,o,i,l,s,u=e.children,c=(t=(0,R.e)(),n=(0,N.HY)(),a=(0,r.useState)(!1),o=a[0],i=a[1],l=null!==n.component,s=(0,I.D9)(l),(0,r.useEffect)((function(){l&&!s&&i(!0)}),[l,s]),(0,r.useEffect)((function(){l?t.shown||i(!0):i(!1)}),[t.shown,l]),(0,r.useMemo)((function(){return[o,i]}),[o]));return r.createElement(M.Provider,{value:c},u)}function F(e){if(e.component){var t=e.component;return r.createElement(t,e.props)}}function B(){var e=(0,r.useContext)(M);if(!e)throw new I.i6("NavbarSecondaryMenuDisplayProvider");var t=e[0],n=e[1],a=(0,r.useCallback)((function(){return n(!1)}),[n]),o=(0,N.HY)();return(0,r.useMemo)((function(){return{shown:t,hide:a,content:F(o)}}),[a,o,t])}function j(e){var t=e.header,n=e.primaryMenu,o=e.secondaryMenu,i=B().shown;return r.createElement("div",{className:"navbar-sidebar"},t,r.createElement("div",{className:(0,a.Z)("navbar-sidebar__items",{"navbar-sidebar__items--show-secondary":i})},r.createElement("div",{className:"navbar-sidebar__item menu"},n),r.createElement("div",{className:"navbar-sidebar__item menu"},o)))}var U=n(2949),z=n(2389);function G(e){return r.createElement("svg",(0,l.Z)({viewBox:"0 0 24 24",width:24,height:24},e),r.createElement("path",{fill:"currentColor",d:"M12,9c1.65,0,3,1.35,3,3s-1.35,3-3,3s-3-1.35-3-3S10.35,9,12,9 M12,7c-2.76,0-5,2.24-5,5s2.24,5,5,5s5-2.24,5-5 S14.76,7,12,7L12,7z M2,13l2,0c0.55,0,1-0.45,1-1s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S1.45,13,2,13z M20,13l2,0c0.55,0,1-0.45,1-1 s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S19.45,13,20,13z M11,2v2c0,0.55,0.45,1,1,1s1-0.45,1-1V2c0-0.55-0.45-1-1-1S11,1.45,11,2z M11,20v2c0,0.55,0.45,1,1,1s1-0.45,1-1v-2c0-0.55-0.45-1-1-1C11.45,19,11,19.45,11,20z M5.99,4.58c-0.39-0.39-1.03-0.39-1.41,0 c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0s0.39-1.03,0-1.41L5.99,4.58z M18.36,16.95 c-0.39-0.39-1.03-0.39-1.41,0c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0c0.39-0.39,0.39-1.03,0-1.41 L18.36,16.95z M19.42,5.99c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06c-0.39,0.39-0.39,1.03,0,1.41 s1.03,0.39,1.41,0L19.42,5.99z M7.05,18.36c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06 c-0.39,0.39-0.39,1.03,0,1.41s1.03,0.39,1.41,0L7.05,18.36z"}))}function H(e){return r.createElement("svg",(0,l.Z)({viewBox:"0 0 24 24",width:24,height:24},e),r.createElement("path",{fill:"currentColor",d:"M9.37,5.51C9.19,6.15,9.1,6.82,9.1,7.5c0,4.08,3.32,7.4,7.4,7.4c0.68,0,1.35-0.09,1.99-0.27C17.45,17.19,14.93,19,12,19 c-3.86,0-7-3.14-7-7C5,9.07,6.81,6.55,9.37,5.51z M12,3c-4.97,0-9,4.03-9,9s4.03,9,9,9s9-4.03,9-9c0-0.46-0.04-0.92-0.1-1.36 c-0.98,1.37-2.58,2.26-4.4,2.26c-2.98,0-5.4-2.42-5.4-5.4c0-1.81,0.89-3.42,2.26-4.4C12.92,3.04,12.46,3,12,3L12,3z"}))}const Z={toggle:"toggle_vylO",toggleButton:"toggleButton_gllP",darkToggleIcon:"darkToggleIcon_wfgR",lightToggleIcon:"lightToggleIcon_pyhR",toggleButtonDisabled:"toggleButtonDisabled_aARS"};function $(e){var t=e.className,n=e.buttonClassName,o=e.value,i=e.onChange,l=(0,z.Z)(),s=(0,u.I)({message:"Switch between dark and light mode (currently {mode})",id:"theme.colorToggle.ariaLabel",description:"The ARIA label for the navbar color mode toggle"},{mode:"dark"===o?(0,u.I)({message:"dark mode",id:"theme.colorToggle.ariaLabel.mode.dark",description:"The name for the dark color mode"}):(0,u.I)({message:"light mode",id:"theme.colorToggle.ariaLabel.mode.light",description:"The name for the light color mode"})});return r.createElement("div",{className:(0,a.Z)(Z.toggle,t)},r.createElement("button",{className:(0,a.Z)("clean-btn",Z.toggleButton,!l&&Z.toggleButtonDisabled,n),type:"button",onClick:function(){return i("dark"===o?"light":"dark")},disabled:!l,title:s,"aria-label":s,"aria-live":"polite"},r.createElement(G,{className:(0,a.Z)(Z.toggleIcon,Z.lightToggleIcon)}),r.createElement(H,{className:(0,a.Z)(Z.toggleIcon,Z.darkToggleIcon)})))}const q=r.memo($),V={darkNavbarColorModeToggle:"darkNavbarColorModeToggle_X3D1"};function W(e){var t=e.className,n=(0,w.L)().navbar.style,a=(0,w.L)().colorMode.disableSwitch,o=(0,U.I)(),i=o.colorMode,l=o.setColorMode;return a?null:r.createElement(q,{className:t,buttonClassName:"dark"===n?V.darkNavbarColorModeToggle:void 0,value:i,onChange:l})}var Q=n(1327);function Y(){return r.createElement(Q.Z,{className:"navbar__brand",imageClassName:"navbar__logo",titleClassName:"navbar__title text--truncate"})}function K(){var e=(0,R.e)();return r.createElement("button",{type:"button","aria-label":(0,u.I)({id:"theme.docs.sidebar.closeSidebarButtonAriaLabel",message:"Close navigation bar",description:"The ARIA label for close button of mobile sidebar"}),className:"clean-btn navbar-sidebar__close",onClick:function(){return e.toggle()}},r.createElement(E,{color:"var(--ifm-color-emphasis-600)"}))}function X(){return r.createElement("div",{className:"navbar-sidebar__brand"},r.createElement(Y,null),r.createElement(W,{className:"margin-right--md"}),r.createElement(K,null))}var J=n(9960),ee=n(4996),te=n(3919);function ne(e,t){return void 0!==e&&void 0!==t&&new RegExp(e,"gi").test(t)}var re=n(9471),ae=["activeBasePath","activeBaseRegex","to","href","label","html","isDropdownLink","prependBaseUrlToHref"];function oe(e){var t=e.activeBasePath,n=e.activeBaseRegex,a=e.to,o=e.href,i=e.label,s=e.html,u=e.isDropdownLink,c=e.prependBaseUrlToHref,p=(0,S.Z)(e,ae),d=(0,ee.Z)(a),f=(0,ee.Z)(t),m=(0,ee.Z)(o,{forcePrependBaseUrl:!0}),h=i&&o&&!(0,te.Z)(o),g=s?{dangerouslySetInnerHTML:{__html:s}}:{children:r.createElement(r.Fragment,null,i,h&&r.createElement(re.Z,u&&{width:12,height:12}))};return o?r.createElement(J.Z,(0,l.Z)({href:c?m:o},p,g)):r.createElement(J.Z,(0,l.Z)({to:d,isNavLink:!0},(t||n)&&{isActive:function(e,t){return n?ne(n,t.pathname):t.pathname.startsWith(f)}},p,g))}var ie=["className","isDropdownItem"],le=["className","isDropdownItem"],se=["mobile","position"];function ue(e){var t=e.className,n=e.isDropdownItem,o=void 0!==n&&n,i=(0,S.Z)(e,ie),s=r.createElement(oe,(0,l.Z)({className:(0,a.Z)(o?"dropdown__link":"navbar__item navbar__link",t),isDropdownLink:o},i));return o?r.createElement("li",null,s):s}function ce(e){var t=e.className,n=(e.isDropdownItem,(0,S.Z)(e,le));return r.createElement("li",{className:"menu__list-item"},r.createElement(oe,(0,l.Z)({className:(0,a.Z)("menu__link",t)},n)))}function pe(e){var t,n=e.mobile,a=void 0!==n&&n,o=(e.position,(0,S.Z)(e,se)),i=a?ce:ue;return r.createElement(i,(0,l.Z)({},o,{activeClassName:null!=(t=o.activeClassName)?t:a?"menu__link--active":"navbar__link--active"}))}var de=n(6043),fe=n(8596),me=n(2263);var he=["items","position","className","onClick"],ge=["items","className","position","onClick"],be=["mobile"];function ve(e,t){return e.some((function(e){return function(e,t){return!!(0,fe.Mg)(e.to,t)||!!ne(e.activeBaseRegex,t)||!(!e.activeBasePath||!t.startsWith(e.activeBasePath))}(e,t)}))}function ye(e){var t,n=e.items,o=e.position,i=e.className,s=(e.onClick,(0,S.Z)(e,he)),u=(0,r.useRef)(null),c=(0,r.useState)(!1),p=c[0],d=c[1];return(0,r.useEffect)((function(){var e=function(e){u.current&&!u.current.contains(e.target)&&d(!1)};return document.addEventListener("mousedown",e),document.addEventListener("touchstart",e),document.addEventListener("focusin",e),function(){document.removeEventListener("mousedown",e),document.removeEventListener("touchstart",e),document.removeEventListener("focusin",e)}}),[u]),r.createElement("div",{ref:u,className:(0,a.Z)("navbar__item","dropdown","dropdown--hoverable",{"dropdown--right":"right"===o,"dropdown--show":p})},r.createElement(oe,(0,l.Z)({"aria-haspopup":"true","aria-expanded":p,role:"button",href:s.to?void 0:"#",className:(0,a.Z)("navbar__link",i)},s,{onClick:s.to?void 0:function(e){return e.preventDefault()},onKeyDown:function(e){"Enter"===e.key&&(e.preventDefault(),d(!p))}}),null!=(t=s.children)?t:s.label),r.createElement("ul",{className:"dropdown__menu"},n.map((function(e,t){return r.createElement(Ue,(0,l.Z)({isDropdownItem:!0,activeClassName:"dropdown__link--active"},e,{key:t}))}))))}function we(e){var t,n,o=e.items,i=e.className,u=(e.position,e.onClick),c=(0,S.Z)(e,ge),p=(n=(0,me.Z)().siteConfig.baseUrl,(0,s.TH)().pathname.replace(n,"/")),d=ve(o,p),f=(0,de.u)({initialState:function(){return!d}}),m=f.collapsed,h=f.toggleCollapsed,g=f.setCollapsed;return(0,r.useEffect)((function(){d&&g(!d)}),[p,d,g]),r.createElement("li",{className:(0,a.Z)("menu__list-item",{"menu__list-item--collapsed":m})},r.createElement(oe,(0,l.Z)({role:"button",className:(0,a.Z)("menu__link menu__link--sublist menu__link--sublist-caret",i)},c,{onClick:function(e){e.preventDefault(),h()}}),null!=(t=c.children)?t:c.label),r.createElement(de.z,{lazy:!0,as:"ul",className:"menu__list",collapsed:m},o.map((function(e,t){return r.createElement(Ue,(0,l.Z)({mobile:!0,isDropdownItem:!0,onClick:u,activeClassName:"menu__link--active"},e,{key:t}))}))))}function _e(e){var t=e.mobile,n=void 0!==t&&t,a=(0,S.Z)(e,be),o=n?we:ye;return r.createElement(o,a)}var Se=n(4711),ke=["width","height"];function Ee(e){var t=e.width,n=void 0===t?20:t,a=e.height,o=void 0===a?20:a,i=(0,S.Z)(e,ke);return r.createElement("svg",(0,l.Z)({viewBox:"0 0 24 24",width:n,height:o,"aria-hidden":!0},i),r.createElement("path",{fill:"currentColor",d:"M12.87 15.07l-2.54-2.51.03-.03c1.74-1.94 2.98-4.17 3.71-6.53H17V4h-7V2H8v2H1v1.99h11.17C11.5 7.92 10.44 9.75 9 11.35 8.07 10.32 7.3 9.19 6.69 8h-2c.73 1.63 1.73 3.17 2.98 4.56l-5.09 5.02L4 19l5-5 3.11 3.11.76-2.04zM18.5 10h-2L12 22h2l1.12-3h4.75L21 22h2l-4.5-12zm-2.62 7l1.62-4.33L19.12 17h-3.24z"}))}const Ce="iconLanguage_nlXk";var xe=["mobile","dropdownItemsBefore","dropdownItemsAfter"];var Te=n(1875);const Pe={searchBox:"searchBox_ZlJk"};function Ae(e){var t=e.children,n=e.className;return r.createElement("div",{className:(0,a.Z)(n,Pe.searchBox)},t)}var Le=n(143),Re=n(8425),Oe=["docId","label","docsPluginId"];var Ie=["sidebarId","label","docsPluginId"];var Ne=["label","to","docsPluginId"];var Me=n(373),De=["mobile","docsPluginId","dropdownActiveClassDisabled","dropdownItemsBefore","dropdownItemsAfter"],Fe=function(e){return e.docs.find((function(t){return t.id===e.mainDocId}))};const Be={default:pe,localeDropdown:function(e){var t=e.mobile,n=e.dropdownItemsBefore,a=e.dropdownItemsAfter,o=(0,S.Z)(e,xe),i=(0,me.Z)().i18n,c=i.currentLocale,p=i.locales,d=i.localeConfigs,f=(0,Se.l)(),m=(0,s.TH)(),h=m.search,g=m.hash,b=p.map((function(e){var n=""+("pathname://"+f.createUrl({locale:e,fullyQualified:!1}))+h+g;return{label:d[e].label,lang:d[e].htmlLang,to:n,target:"_self",autoAddBaseUrl:!1,className:e===c?t?"menu__link--active":"dropdown__link--active":""}})),v=[].concat(n,b,a),y=t?(0,u.I)({message:"Languages",id:"theme.navbar.mobileLanguageDropdown.label",description:"The label for the mobile language switcher dropdown"}):d[c].label;return r.createElement(_e,(0,l.Z)({},o,{mobile:t,label:r.createElement(r.Fragment,null,r.createElement(Ee,{className:Ce}),y),items:v}))},search:function(e){var t=e.mobile,n=e.className;return t?null:r.createElement(Ae,{className:n},r.createElement(Te.Z,null))},dropdown:_e,html:function(e){var t=e.value,n=e.className,o=e.mobile,i=void 0!==o&&o,l=e.isDropdownItem,s=void 0!==l&&l,u=s?"li":"div";return r.createElement(u,{className:(0,a.Z)({navbar__item:!i&&!s,"menu__list-item":i},n),dangerouslySetInnerHTML:{__html:t}})},doc:function(e){var t=e.docId,n=e.label,a=e.docsPluginId,o=(0,S.Z)(e,Oe),i=(0,Le.Iw)(a).activeDoc,s=(0,Re.vY)(t,a);return null===s?null:r.createElement(pe,(0,l.Z)({exact:!0},o,{isActive:function(){return(null==i?void 0:i.path)===s.path||!(null==i||!i.sidebar)&&i.sidebar===s.sidebar},label:null!=n?n:s.id,to:s.path}))},docSidebar:function(e){var t=e.sidebarId,n=e.label,a=e.docsPluginId,o=(0,S.Z)(e,Ie),i=(0,Le.Iw)(a).activeDoc,s=(0,Re.oz)(t,a).link;if(!s)throw new Error('DocSidebarNavbarItem: Sidebar with ID "'+t+"\" doesn't have anything to be linked to.");return r.createElement(pe,(0,l.Z)({exact:!0},o,{isActive:function(){return(null==i?void 0:i.sidebar)===t},label:null!=n?n:s.label,to:s.path}))},docsVersion:function(e){var t=e.label,n=e.to,a=e.docsPluginId,o=(0,S.Z)(e,Ne),i=(0,Re.lO)(a)[0],s=null!=t?t:i.label,u=null!=n?n:function(e){return e.docs.find((function(t){return t.id===e.mainDocId}))}(i).path;return r.createElement(pe,(0,l.Z)({},o,{label:s,to:u}))},docsVersionDropdown:function(e){var t=e.mobile,n=e.docsPluginId,a=e.dropdownActiveClassDisabled,o=e.dropdownItemsBefore,i=e.dropdownItemsAfter,c=(0,S.Z)(e,De),p=(0,s.TH)(),d=p.search,f=p.hash,m=(0,Le.Iw)(n),h=(0,Le.gB)(n),g=(0,Me.J)(n).savePreferredVersionName,b=h.map((function(e){var t,n=null!=(t=m.alternateDocVersions[e.name])?t:Fe(e);return{label:e.label,to:""+n.path+d+f,isActive:function(){return e===m.activeVersion},onClick:function(){return g(e.name)}}})),v=[].concat(o,b,i),y=(0,Re.lO)(n)[0],w=t&&v.length>1?(0,u.I)({id:"theme.navbar.mobileVersionsDropdown.label",message:"Versions",description:"The label for the navbar versions dropdown on mobile view"}):y.label,_=t&&v.length>1?void 0:Fe(y).path;return v.length<=1?r.createElement(pe,(0,l.Z)({},c,{mobile:t,label:w,to:_,isActive:a?function(){return!1}:void 0})):r.createElement(_e,(0,l.Z)({},c,{mobile:t,label:w,to:_,items:v,isActive:a?function(){return!1}:void 0}))}};var je=["type"];function Ue(e){var t=e.type,n=(0,S.Z)(e,je),a=function(e,t){return e&&"default"!==e?e:"items"in t?"dropdown":"default"}(t,n),o=Be[a];if(!o)throw new Error('No NavbarItem component found for type "'+t+'".');return r.createElement(o,n)}function ze(){var e=(0,R.e)(),t=(0,w.L)().navbar.items;return r.createElement("ul",{className:"menu__list"},t.map((function(t,n){return r.createElement(Ue,(0,l.Z)({mobile:!0},t,{onClick:function(){return e.toggle()},key:n}))})))}function Ge(e){return r.createElement("button",(0,l.Z)({},e,{type:"button",className:"clean-btn navbar-sidebar__back"}),r.createElement(u.Z,{id:"theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel",description:"The label of the back button to return to main menu, inside the mobile navbar sidebar secondary menu (notably used to display the docs sidebar)"},"\u2190 Back to main menu"))}function He(){var e=0===(0,w.L)().navbar.items.length,t=B();return r.createElement(r.Fragment,null,!e&&r.createElement(Ge,{onClick:function(){return t.hide()}}),t.content)}function Ze(){var e,t=(0,R.e)();return void 0===(e=t.shown)&&(e=!0),(0,r.useEffect)((function(){return document.body.style.overflow=e?"hidden":"visible",function(){document.body.style.overflow="visible"}}),[e]),t.shouldRender?r.createElement(j,{header:r.createElement(X,null),primaryMenu:r.createElement(ze,null),secondaryMenu:r.createElement(He,null)}):null}const $e={navbarHideable:"navbarHideable_m1mJ",navbarHidden:"navbarHidden_jGov"};function qe(e){return r.createElement("div",(0,l.Z)({role:"presentation"},e,{className:(0,a.Z)("navbar-sidebar__backdrop",e.className)}))}function Ve(e){var t=e.children,n=(0,w.L)().navbar,o=n.hideOnScroll,i=n.style,l=(0,R.e)(),s=function(e){var t=(0,r.useState)(e),n=t[0],a=t[1],o=(0,r.useRef)(!1),i=(0,r.useRef)(0),l=(0,r.useCallback)((function(e){null!==e&&(i.current=e.getBoundingClientRect().height)}),[]);return(0,O.RF)((function(t,n){var r=t.scrollY;if(e)if(r=l?a(!1):r+u0&&r.createElement(bt,{links:n}),logo:a&&r.createElement(_t,{logo:a}),copyright:t&&r.createElement(St,{copyright:t})})}const Ct=r.memo(Et);var xt=(0,I.Qc)([U.S,_.pl,O.OC,Me.L5,i.VC,function(e){var t=e.children;return r.createElement(N.n2,null,r.createElement(R.M,null,r.createElement(D,null,t)))}]);function Tt(e){var t=e.children;return r.createElement(xt,null,t)}function Pt(e){var t=e.error,n=e.tryAgain;return r.createElement("main",{className:"container margin-vert--xl"},r.createElement("div",{className:"row"},r.createElement("div",{className:"col col--6 col--offset-3"},r.createElement("h1",{className:"hero__title"},r.createElement(u.Z,{id:"theme.ErrorPageContent.title",description:"The title of the fallback page when the page crashed"},"This page crashed.")),r.createElement("div",{className:"margin-vert--lg"},r.createElement(Ke,{onClick:n,className:"button button--primary shadow--lw"})),r.createElement("hr",null),r.createElement("div",{className:"margin-vert--md"},r.createElement(Xe,{error:t})))))}const At={mainWrapper:"mainWrapper_z2l0"};function Lt(e){var t=e.children,n=e.noFooter,l=e.wrapperClassName,s=e.title,u=e.description;return(0,b.t)(),r.createElement(Tt,null,r.createElement(i.d,{title:s,description:u}),r.createElement(y,null),r.createElement(L,null),r.createElement(st,null),r.createElement("div",{id:p,className:(0,a.Z)(g.k.wrapper.main,At.mainWrapper,l)},r.createElement(o.Z,{fallback:function(e){return r.createElement(Pt,e)}},t)),!n&&r.createElement(Ct,null))}},1327:(e,t,n)=>{"use strict";n.d(t,{Z:()=>f});var r=n(7462),a=n(3366),o=n(7294),i=n(9960),l=n(4996),s=n(2263),u=n(6668),c=n(941),p=["imageClassName","titleClassName"];function d(e){var t=e.logo,n=e.alt,r=e.imageClassName,a={light:(0,l.Z)(t.src),dark:(0,l.Z)(t.srcDark||t.src)},i=o.createElement(c.Z,{className:t.className,sources:a,height:t.height,width:t.width,alt:n,style:t.style});return r?o.createElement("div",{className:r},i):i}function f(e){var t,n=(0,s.Z)().siteConfig.title,c=(0,u.L)().navbar,f=c.title,m=c.logo,h=e.imageClassName,g=e.titleClassName,b=(0,a.Z)(e,p),v=(0,l.Z)((null==m?void 0:m.href)||"/"),y=f?"":n,w=null!=(t=null==m?void 0:m.alt)?t:y;return o.createElement(i.Z,(0,r.Z)({to:v},b,(null==m?void 0:m.target)&&{target:m.target}),m&&o.createElement(d,{logo:m,alt:w,imageClassName:h}),null!=f&&o.createElement("b",{className:g},f))}},197:(e,t,n)=>{"use strict";n.d(t,{Z:()=>o});var r=n(7294),a=n(5742);function o(e){var t=e.locale,n=e.version,o=e.tag,i=t;return r.createElement(a.Z,null,t&&r.createElement("meta",{name:"docusaurus_locale",content:t}),n&&r.createElement("meta",{name:"docusaurus_version",content:n}),o&&r.createElement("meta",{name:"docusaurus_tag",content:o}),i&&r.createElement("meta",{name:"docsearch:language",content:i}),n&&r.createElement("meta",{name:"docsearch:version",content:n}),o&&r.createElement("meta",{name:"docsearch:docusaurus_tag",content:o}))}},941:(e,t,n)=>{"use strict";n.d(t,{Z:()=>p});var r=n(7462),a=n(3366),o=n(7294),i=n(6010),l=n(2389),s=n(2949);const u={themedImage:"themedImage_ToTc","themedImage--light":"themedImage--light_HNdA","themedImage--dark":"themedImage--dark_i4oU"};var c=["sources","className","alt"];function p(e){var t=(0,l.Z)(),n=(0,s.I)().colorMode,p=e.sources,d=e.className,f=e.alt,m=(0,a.Z)(e,c),h=t?"dark"===n?["dark"]:["light"]:["light","dark"];return o.createElement(o.Fragment,null,h.map((function(e){return o.createElement("img",(0,r.Z)({key:e,src:p[e],alt:f,className:(0,i.Z)(u.themedImage,u["themedImage--"+e],d)},m))})))}},6043:(e,t,n)=>{"use strict";n.d(t,{u:()=>p,z:()=>y});var r=n(7462),a=n(3366),o=n(7294),i=n(412),l=n(1442),s=["collapsed"],u=["lazy"],c="ease-in-out";function p(e){var t=e.initialState,n=(0,o.useState)(null!=t&&t),r=n[0],a=n[1],i=(0,o.useCallback)((function(){a((function(e){return!e}))}),[]);return{collapsed:r,setCollapsed:a,toggleCollapsed:i}}var d={display:"none",overflow:"hidden",height:"0px"},f={display:"block",overflow:"visible",height:"auto"};function m(e,t){var n=t?d:f;e.style.display=n.display,e.style.overflow=n.overflow,e.style.height=n.height}function h(e){var t=e.collapsibleRef,n=e.collapsed,r=e.animation,a=(0,o.useRef)(!1);(0,o.useEffect)((function(){var e,o=t.current;function i(){var e,t,n=o.scrollHeight,a=null!=(e=null==r?void 0:r.duration)?e:function(e){if((0,l.n)())return 1;var t=e/36;return Math.round(10*(4+15*Math.pow(t,.25)+t/5))}(n);return{transition:"height "+a+"ms "+(null!=(t=null==r?void 0:r.easing)?t:c),height:n+"px"}}function s(){var e=i();o.style.transition=e.transition,o.style.height=e.height}if(!a.current)return m(o,n),void(a.current=!0);return o.style.willChange="height",e=requestAnimationFrame((function(){n?(s(),requestAnimationFrame((function(){o.style.height=d.height,o.style.overflow=d.overflow}))):(o.style.display="block",requestAnimationFrame((function(){s()})))})),function(){return cancelAnimationFrame(e)}}),[t,n,r])}function g(e){if(!i.Z.canUseDOM)return e?d:f}function b(e){var t=e.as,n=void 0===t?"div":t,r=e.collapsed,a=e.children,i=e.animation,l=e.onCollapseTransitionEnd,s=e.className,u=e.disableSSRStyle,c=(0,o.useRef)(null);return h({collapsibleRef:c,collapsed:r,animation:i}),o.createElement(n,{ref:c,style:u?void 0:g(r),onTransitionEnd:function(e){"height"===e.propertyName&&(m(c.current,r),null==l||l(r))},className:s},a)}function v(e){var t=e.collapsed,n=(0,a.Z)(e,s),i=(0,o.useState)(!t),l=i[0],u=i[1],c=(0,o.useState)(t),p=c[0],d=c[1];return(0,o.useLayoutEffect)((function(){t||u(!0)}),[t]),(0,o.useLayoutEffect)((function(){l&&d(t)}),[l,t]),l?o.createElement(b,(0,r.Z)({},n,{collapsed:p})):null}function y(e){var t=e.lazy,n=(0,a.Z)(e,u),r=t?v:b;return o.createElement(r,n)}},9689:(e,t,n)=>{"use strict";n.d(t,{nT:()=>m,pl:()=>f});var r=n(7294),a=n(2389),o=n(12),i=n(9688),l=n(6668),s=(0,o.WA)("docusaurus.announcement.dismiss"),u=(0,o.WA)("docusaurus.announcement.id"),c=function(){return"true"===s.get()},p=function(e){return s.set(String(e))},d=r.createContext(null);function f(e){var t=e.children,n=function(){var e=(0,l.L)().announcementBar,t=(0,a.Z)(),n=(0,r.useState)((function(){return!!t&&c()})),o=n[0],i=n[1];(0,r.useEffect)((function(){i(c())}),[]);var s=(0,r.useCallback)((function(){p(!0),i(!0)}),[]);return(0,r.useEffect)((function(){if(e){var t=e.id,n=u.get();"annoucement-bar"===n&&(n="announcement-bar");var r=t!==n;u.set(t),r&&p(!1),!r&&c()||i(!1)}}),[e]),(0,r.useMemo)((function(){return{isActive:!!e&&!o,close:s}}),[e,o,s])}();return r.createElement(d.Provider,{value:n},t)}function m(){var e=(0,r.useContext)(d);if(!e)throw new i.i6("AnnouncementBarProvider");return e}},2949:(e,t,n)=>{"use strict";n.d(t,{I:()=>g,S:()=>h});var r=n(7294),a=n(412),o=n(9688),i=n(12),l=n(6668),s=r.createContext(void 0),u="theme",c=(0,i.WA)(u),p={light:"light",dark:"dark"},d=function(e){return e===p.dark?p.dark:p.light},f=function(e){return a.Z.canUseDOM?d(document.documentElement.getAttribute("data-theme")):d(e)},m=function(e){c.set(d(e))};function h(e){var t=e.children,n=function(){var e=(0,l.L)().colorMode,t=e.defaultMode,n=e.disableSwitch,a=e.respectPrefersColorScheme,o=(0,r.useState)(f(t)),i=o[0],s=o[1];(0,r.useEffect)((function(){n&&c.del()}),[n]);var h=(0,r.useCallback)((function(e,n){void 0===n&&(n={});var r=n.persist,o=void 0===r||r;e?(s(e),o&&m(e)):(s(a?window.matchMedia("(prefers-color-scheme: dark)").matches?p.dark:p.light:t),c.del())}),[a,t]);(0,r.useEffect)((function(){document.documentElement.setAttribute("data-theme",d(i))}),[i]),(0,r.useEffect)((function(){if(!n){var e=function(e){if(e.key===u){var t=c.get();null!==t&&h(d(t))}};return window.addEventListener("storage",e),function(){return window.removeEventListener("storage",e)}}}),[n,h]);var g=(0,r.useRef)(!1);return(0,r.useEffect)((function(){if(!n||a){var e=window.matchMedia("(prefers-color-scheme: dark)"),t=function(){window.matchMedia("print").matches||g.current?g.current=window.matchMedia("print").matches:h(null)};return e.addListener(t),function(){return e.removeListener(t)}}}),[h,n,a]),(0,r.useMemo)((function(){return{colorMode:i,setColorMode:h,get isDarkTheme(){return i===p.dark},setLightTheme:function(){h(p.light)},setDarkTheme:function(){h(p.dark)}}}),[i,h])}();return r.createElement(s.Provider,{value:n},t)}function g(){var e=(0,r.useContext)(s);if(null==e)throw new o.i6("ColorModeProvider","Please see https://docusaurus.io/docs/api/themes/configuration#use-color-mode.");return e}},373:(e,t,n)=>{"use strict";n.d(t,{J:()=>v,L5:()=>g});var r=n(7294),a=n(143),o=n(9935),i=n(6668),l=n(8425),s=n(9688),u=n(12),c=function(e){return"docs-preferred-version-"+e},p={save:function(e,t,n){(0,u.WA)(c(e),{persistence:t}).set(n)},read:function(e,t){return(0,u.WA)(c(e),{persistence:t}).get()},clear:function(e,t){(0,u.WA)(c(e),{persistence:t}).del()}},d=function(e){return Object.fromEntries(e.map((function(e){return[e,{preferredVersionName:null}]})))};var f=r.createContext(null);function m(){var e=(0,a._r)(),t=(0,i.L)().docs.versionPersistence,n=(0,r.useMemo)((function(){return Object.keys(e)}),[e]),o=(0,r.useState)((function(){return d(n)})),l=o[0],s=o[1];return(0,r.useEffect)((function(){s(function(e){var t=e.pluginIds,n=e.versionPersistence,r=e.allDocsData;return Object.fromEntries(t.map((function(e){return[e,(t=e,a=p.read(t,n),r[t].versions.some((function(e){return e.name===a}))?{preferredVersionName:a}:(p.clear(t,n),{preferredVersionName:null}))];var t,a})))}({allDocsData:e,versionPersistence:t,pluginIds:n}))}),[e,t,n]),[l,(0,r.useMemo)((function(){return{savePreferredVersion:function(e,n){p.save(e,t,n),s((function(t){var r;return Object.assign({},t,((r={})[e]={preferredVersionName:n},r))}))}}}),[t])]}function h(e){var t=e.children,n=m();return r.createElement(f.Provider,{value:n},t)}function g(e){var t=e.children;return l.cE?r.createElement(h,null,t):r.createElement(r.Fragment,null,t)}function b(){var e=(0,r.useContext)(f);if(!e)throw new s.i6("DocsPreferredVersionContextProvider");return e}function v(e){var t;void 0===e&&(e=o.m);var n=(0,a.zh)(e),i=b(),l=i[0],s=i[1],u=l[e].preferredVersionName;return{preferredVersion:null!=(t=n.versions.find((function(e){return e.name===u})))?t:null,savePreferredVersionName:(0,r.useCallback)((function(t){s.savePreferredVersion(e,t)}),[s,e])}}},1116:(e,t,n)=>{"use strict";n.d(t,{V:()=>s,b:()=>l});var r=n(7294),a=n(9688),o=Symbol("EmptyContext"),i=r.createContext(o);function l(e){var t=e.children,n=e.name,a=e.items,o=(0,r.useMemo)((function(){return n&&a?{name:n,items:a}:null}),[n,a]);return r.createElement(i.Provider,{value:o},t)}function s(){var e=(0,r.useContext)(i);if(e===o)throw new a.i6("DocsSidebarProvider");return e}},2961:(e,t,n)=>{"use strict";n.d(t,{M:()=>d,e:()=>f});var r=n(7294),a=n(3102),o=n(7524),i=n(6550),l=(n(1688),n(9688));function s(e){!function(e){var t=(0,i.k6)(),n=(0,l.zX)(e);(0,r.useEffect)((function(){return t.block((function(e,t){return n(e,t)}))}),[t,n])}((function(t,n){if("POP"===n)return e(t,n)}))}var u=n(6668),c=r.createContext(void 0);function p(){var e,t=(e=(0,a.HY)(),0===(0,u.L)().navbar.items.length&&!e.component),n=(0,o.i)(),i=!t&&"mobile"===n,l=(0,r.useState)(!1),c=l[0],p=l[1];s((function(){if(c)return p(!1),!1}));var d=(0,r.useCallback)((function(){p((function(e){return!e}))}),[]);return(0,r.useEffect)((function(){"desktop"===n&&p(!1)}),[n]),(0,r.useMemo)((function(){return{disabled:t,shouldRender:i,toggle:d,shown:c}}),[t,i,d,c])}function d(e){var t=e.children,n=p();return r.createElement(c.Provider,{value:n},t)}function f(){var e=r.useContext(c);if(void 0===e)throw new l.i6("NavbarMobileSidebarProvider");return e}},3102:(e,t,n)=>{"use strict";n.d(t,{HY:()=>l,Zo:()=>s,n2:()=>i});var r=n(7294),a=n(9688),o=r.createContext(null);function i(e){var t=e.children,n=(0,r.useState)({component:null,props:null});return r.createElement(o.Provider,{value:n},t)}function l(){var e=(0,r.useContext)(o);if(!e)throw new a.i6("NavbarSecondaryMenuContentProvider");return e[0]}function s(e){var t=e.component,n=e.props,i=(0,r.useContext)(o);if(!i)throw new a.i6("NavbarSecondaryMenuContentProvider");var l=i[1],s=(0,a.Ql)(n);return(0,r.useEffect)((function(){l({component:t,props:s})}),[l,t,s]),(0,r.useEffect)((function(){return function(){return l({component:null,props:null})}}),[l]),null}},9727:(e,t,n)=>{"use strict";n.d(t,{h:()=>a,t:()=>o});var r=n(7294),a="navigation-with-keyboard";function o(){(0,r.useEffect)((function(){function e(e){"keydown"===e.type&&"Tab"===e.key&&document.body.classList.add(a),"mousedown"===e.type&&document.body.classList.remove(a)}return document.addEventListener("keydown",e),document.addEventListener("mousedown",e),function(){document.body.classList.remove(a),document.removeEventListener("keydown",e),document.removeEventListener("mousedown",e)}}),[])}},7524:(e,t,n)=>{"use strict";n.d(t,{i:()=>u});var r=n(7294),a=n(412),o={desktop:"desktop",mobile:"mobile",ssr:"ssr"},i=996;function l(){return a.Z.canUseDOM?window.innerWidth>i?o.desktop:o.mobile:o.ssr}var s=!1;function u(){var e=(0,r.useState)((function(){return s?"ssr":l()})),t=e[0],n=e[1];return(0,r.useEffect)((function(){function e(){n(l())}var t=s?window.setTimeout(e,1e3):void 0;return window.addEventListener("resize",e),function(){window.removeEventListener("resize",e),clearTimeout(t)}}),[]),t}},5281:(e,t,n)=>{"use strict";n.d(t,{k:()=>r});var r={page:{blogListPage:"blog-list-page",blogPostPage:"blog-post-page",blogTagsListPage:"blog-tags-list-page",blogTagPostListPage:"blog-tags-post-list-page",docsDocPage:"docs-doc-page",docsTagsListPage:"docs-tags-list-page",docsTagDocListPage:"docs-tags-doc-list-page",mdxPage:"mdx-page"},wrapper:{main:"main-wrapper",blogPages:"blog-wrapper",docsPages:"docs-wrapper",mdxPages:"mdx-wrapper"},common:{editThisPage:"theme-edit-this-page",lastUpdated:"theme-last-updated",backToTopButton:"theme-back-to-top-button",codeBlock:"theme-code-block",admonition:"theme-admonition",admonitionType:function(e){return"theme-admonition-"+e}},layout:{},docs:{docVersionBanner:"theme-doc-version-banner",docVersionBadge:"theme-doc-version-badge",docBreadcrumbs:"theme-doc-breadcrumbs",docMarkdown:"theme-doc-markdown",docTocMobile:"theme-doc-toc-mobile",docTocDesktop:"theme-doc-toc-desktop",docFooter:"theme-doc-footer",docFooterTagsRow:"theme-doc-footer-tags-row",docFooterEditMetaRow:"theme-doc-footer-edit-meta-row",docSidebarContainer:"theme-doc-sidebar-container",docSidebarMenu:"theme-doc-sidebar-menu",docSidebarItemCategory:"theme-doc-sidebar-item-category",docSidebarItemLink:"theme-doc-sidebar-item-link",docSidebarItemCategoryLevel:function(e){return"theme-doc-sidebar-item-category-level-"+e},docSidebarItemLinkLevel:function(e){return"theme-doc-sidebar-item-link-level-"+e}},blog:{}}},1442:(e,t,n)=>{"use strict";function r(){return window.matchMedia("(prefers-reduced-motion: reduce)").matches}n.d(t,{n:()=>r})},8425:(e,t,n)=>{"use strict";function r(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n=e.length?{done:!0}:{done:!1,value:e[a++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}n.d(t,{Wl:()=>m,_F:()=>b,cE:()=>f,hI:()=>k,lO:()=>w,vY:()=>S,oz:()=>_,s1:()=>y});var o=n(7294),i=n(6550),l=n(8790),s=n(143),u=n(373),c=n(1116);function p(e){return Array.from(new Set(e))}var d=n(8596),f=!!s._r;function m(e){if(e.href)return e.href;for(var t,n=a(e.items);!(t=n()).done;){var r=t.value;if("link"===r.type)return r.href;if("category"===r.type){var o=m(r);if(o)return o}}}var h=function(e,t){return void 0!==e&&(0,d.Mg)(e,t)},g=function(e,t){return e.some((function(e){return b(e,t)}))};function b(e,t){return"link"===e.type?h(e.href,t):"category"===e.type&&(h(e.href,t)||g(e.items,t))}function v(e){var t=e.sidebarItems,n=e.pathname,r=e.onlyCategories,o=void 0!==r&&r,i=[];return function e(t){for(var r,l=a(t);!(r=l()).done;){var s=r.value;if("category"===s.type&&((0,d.Mg)(s.href,n)||e(s.items))||"link"===s.type&&(0,d.Mg)(s.href,n))return o&&"category"!==s.type||i.unshift(s),!0}return!1}(t),i}function y(){var e,t=(0,c.V)(),n=(0,i.TH)().pathname;return!1!==(null==(e=(0,s.gA)())?void 0:e.pluginData.breadcrumbs)&&t?v({sidebarItems:t.items,pathname:n}):null}function w(e){var t=(0,s.Iw)(e).activeVersion,n=(0,u.J)(e).preferredVersion,r=(0,s.yW)(e);return(0,o.useMemo)((function(){return p([t,n,r].filter(Boolean))}),[t,n,r])}function _(e,t){var n=w(t);return(0,o.useMemo)((function(){var t=n.flatMap((function(e){return e.sidebars?Object.entries(e.sidebars):[]})),r=t.find((function(t){return t[0]===e}));if(!r)throw new Error("Can't find any sidebar with id \""+e+'" in version'+(n.length>1?"s":"")+" "+n.map((function(e){return e.name})).join(", ")+'".\nAvailable sidebar ids are:\n- '+t.map((function(e){return e[0]})).join("\n- "));return r[1]}),[e,n])}function S(e,t){var n=w(t);return(0,o.useMemo)((function(){var t=n.flatMap((function(e){return e.docs})),r=t.find((function(t){return t.id===e}));if(!r){if(n.flatMap((function(e){return e.draftIds})).includes(e))return null;throw new Error("Couldn't find any doc with id \""+e+'" in version'+(n.length>1?"s":"")+' "'+n.map((function(e){return e.name})).join(", ")+'".\nAvailable doc ids are:\n- '+p(t.map((function(e){return e.id}))).join("\n- "))}return r}),[e,n])}function k(e){var t=e.route,n=e.versionMetadata,r=(0,i.TH)(),a=t.routes,o=a.find((function(e){return(0,i.LX)(r.pathname,e)}));if(!o)return null;var s=o.sidebar,u=s?n.docsSidebars[s]:void 0;return{docElement:(0,l.H)(a),sidebarName:s,sidebarItems:u}}},1944:(e,t,n)=>{"use strict";n.d(t,{FG:()=>d,d:()=>c,VC:()=>f});var r=n(7294),a=n(6010),o=n(5742),i=n(226);function l(){var e=r.useContext(i._);if(!e)throw new Error("Unexpected: no Docusaurus route context found");return e}var s=n(4996),u=n(2263);function c(e){var t=e.title,n=e.description,a=e.keywords,i=e.image,l=e.children,c=function(e){var t=(0,u.Z)().siteConfig,n=t.title,r=t.titleDelimiter;return null!=e&&e.trim().length?e.trim()+" "+r+" "+n:n}(t),p=(0,s.C)().withBaseUrl,d=i?p(i,{absolute:!0}):void 0;return r.createElement(o.Z,null,t&&r.createElement("title",null,c),t&&r.createElement("meta",{property:"og:title",content:c}),n&&r.createElement("meta",{name:"description",content:n}),n&&r.createElement("meta",{property:"og:description",content:n}),a&&r.createElement("meta",{name:"keywords",content:Array.isArray(a)?a.join(","):a}),d&&r.createElement("meta",{property:"og:image",content:d}),d&&r.createElement("meta",{name:"twitter:image",content:d}),l)}var p=r.createContext(void 0);function d(e){var t=e.className,n=e.children,i=r.useContext(p),l=(0,a.Z)(i,t);return r.createElement(p.Provider,{value:l},r.createElement(o.Z,null,r.createElement("html",{className:l})),n)}function f(e){var t=e.children,n=l(),o="plugin-"+n.plugin.name.replace(/docusaurus-(?:plugin|theme)-(?:content-)?/gi,""),i="plugin-id-"+n.plugin.id;return r.createElement(d,{className:(0,a.Z)(o,i)},t)}},9688:(e,t,n)=>{"use strict";n.d(t,{i6:()=>f,Qc:()=>h,zX:()=>p,D9:()=>d,Ql:()=>m});var r=n(6528),a=n(4578);function o(e){return o=Object.setPrototypeOf?Object.getPrototypeOf.bind():function(e){return e.__proto__||Object.getPrototypeOf(e)},o(e)}var i=n(9611);function l(e,t,n){return l=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}()?Reflect.construct.bind():function(e,t,n){var r=[null];r.push.apply(r,t);var a=new(Function.bind.apply(e,r));return n&&(0,i.Z)(a,n.prototype),a},l.apply(null,arguments)}function s(e){var t="function"==typeof Map?new Map:void 0;return s=function(e){if(null===e||!function(e){try{return-1!==Function.toString.call(e).indexOf("[native code]")}catch(t){return"function"==typeof e}}(e))return e;if("function"!=typeof e)throw new TypeError("Super expression must either be null or a function");if(void 0!==t){if(t.has(e))return t.get(e);t.set(e,n)}function n(){return l(e,arguments,o(this).constructor)}return n.prototype=Object.create(e.prototype,{constructor:{value:n,enumerable:!1,writable:!0,configurable:!0}}),(0,i.Z)(n,e)},s(e)}var u=n(7294),c=n(412).Z.canUseDOM?u.useLayoutEffect:u.useEffect;function p(e){var t=(0,u.useRef)(e);return c((function(){t.current=e}),[e]),(0,u.useCallback)((function(){return t.current.apply(t,arguments)}),[])}function d(e){var t=(0,u.useRef)();return c((function(){t.current=e})),t.current}var f=function(e){function t(t,n){var a,o,i;return(i=e.call(this)||this).name="ReactContextError",i.message="Hook "+(null!=(a=null==(o=i.stack)||null==(o=o.split("\n")[1])||null==(o=o.match((0,r.Z)(/at (?:\w+\.)?(\w+)/,{name:1})))?void 0:o.groups.name)?a:"")+" is called outside the <"+t+">. "+(null!=n?n:""),i}return(0,a.Z)(t,e),t}(s(Error));function m(e){var t=Object.entries(e);return t.sort((function(e,t){return e[0].localeCompare(t[0])})),(0,u.useMemo)((function(){return e}),t.flat())}function h(e){return function(t){var n=t.children;return u.createElement(u.Fragment,null,e.reduceRight((function(e,t){return u.createElement(t,null,e)}),n))}}},8596:(e,t,n)=>{"use strict";n.d(t,{Mg:()=>i,Ns:()=>l});var r=n(7294),a=n(723),o=n(2263);function i(e,t){var n=function(e){var t;return null==(t=!e||e.endsWith("/")?e:e+"/")?void 0:t.toLowerCase()};return n(e)===n(t)}function l(){var e=(0,o.Z)().siteConfig.baseUrl;return(0,r.useMemo)((function(){return function(e){var t=e.baseUrl;function n(e){return e.path===t&&!0===e.exact}function r(e){return e.path===t&&!e.exact}return function e(t){if(0!==t.length)return t.find(n)||e(t.filter(r).flatMap((function(e){var t;return null!=(t=e.routes)?t:[]})))}(e.routes)}({routes:a.Z,baseUrl:e})}),[e])}},2466:(e,t,n)=>{"use strict";n.d(t,{Ct:()=>d,OC:()=>s,RF:()=>p});var r=n(7294),a=n(412),o=n(2389),i=n(9688);var l=r.createContext(void 0);function s(e){var t,n=e.children,a=(t=(0,r.useRef)(!0),(0,r.useMemo)((function(){return{scrollEventsEnabledRef:t,enableScrollEvents:function(){t.current=!0},disableScrollEvents:function(){t.current=!1}}}),[]));return r.createElement(l.Provider,{value:a},n)}function u(){var e=(0,r.useContext)(l);if(null==e)throw new i.i6("ScrollControllerProvider");return e}var c=function(){return a.Z.canUseDOM?{scrollX:window.pageXOffset,scrollY:window.pageYOffset}:null};function p(e,t){void 0===t&&(t=[]);var n=u().scrollEventsEnabledRef,a=(0,r.useRef)(c()),o=(0,i.zX)(e);(0,r.useEffect)((function(){var e=function(){if(n.current){var e=c();o(e,a.current),a.current=e}},t={passive:!0};return e(),window.addEventListener("scroll",e,t),function(){return window.removeEventListener("scroll",e,t)}}),[o,n].concat(t))}function d(){var e=(0,r.useRef)(null),t=(0,o.Z)()&&"smooth"===getComputedStyle(document.documentElement).scrollBehavior;return{startScroll:function(n){e.current=t?function(e){return window.scrollTo({top:e,behavior:"smooth"}),function(){}}(n):function(e){var t=null,n=document.documentElement.scrollTop>e;return function r(){var a=document.documentElement.scrollTop;(n&&a>e||!n&&a{"use strict";n.d(t,{HX:()=>r,os:()=>a});n(2263);var r="default";function a(e,t){return"docs-"+e+"-"+t}},12:(e,t,n)=>{"use strict";n.d(t,{WA:()=>s});n(7294),n(1688);var r="localStorage";function a(e){var t=e.key,n=e.oldValue,r=e.newValue,a=e.storage;if(n!==r){var o=document.createEvent("StorageEvent");o.initStorageEvent("storage",!1,!1,t,n,r,window.location.href,a),window.dispatchEvent(o)}}function o(e){if(void 0===e&&(e=r),"undefined"==typeof window)throw new Error("Browser storage is not available on Node.js/Docusaurus SSR process.");if("none"===e)return null;try{return window[e]}catch(n){return t=n,i||(console.warn("Docusaurus browser storage is not available.\nPossible reasons: running Docusaurus in an iframe, in an incognito browser session, or using too strict browser privacy settings.",t),i=!0),null}var t}var i=!1;var l={get:function(){return null},set:function(){},del:function(){},listen:function(){return function(){}}};function s(e,t){if("undefined"==typeof window)return function(e){function t(){throw new Error('Illegal storage API usage for storage key "'+e+'".\nDocusaurus storage APIs are not supposed to be called on the server-rendering process.\nPlease only call storage APIs in effects and event handlers.')}return{get:t,set:t,del:t,listen:t}}(e);var n=o(null==t?void 0:t.persistence);return null===n?l:{get:function(){try{return n.getItem(e)}catch(t){return console.error("Docusaurus storage error, can't get key="+e,t),null}},set:function(t){try{var r=n.getItem(e);n.setItem(e,t),a({key:e,oldValue:r,newValue:t,storage:n})}catch(o){console.error("Docusaurus storage error, can't set "+e+"="+t,o)}},del:function(){try{var t=n.getItem(e);n.removeItem(e),a({key:e,oldValue:t,newValue:null,storage:n})}catch(r){console.error("Docusaurus storage error, can't delete key="+e,r)}},listen:function(t){try{var r=function(r){r.storageArea===n&&r.key===e&&t(r)};return window.addEventListener("storage",r),function(){return window.removeEventListener("storage",r)}}catch(a){return console.error("Docusaurus storage error, can't listen for changes of key="+e,a),function(){}}}}}},4711:(e,t,n)=>{"use strict";n.d(t,{l:()=>i});var r=n(2263),a=n(6550),o=n(8780);function i(){var e=(0,r.Z)(),t=e.siteConfig,n=t.baseUrl,i=t.url,l=t.trailingSlash,s=e.i18n,u=s.defaultLocale,c=s.currentLocale,p=(0,a.TH)().pathname,d=(0,o.applyTrailingSlash)(p,{trailingSlash:l,baseUrl:n}),f=c===u?n:n.replace("/"+c+"/","/"),m=d.replace(n,"");return{createUrl:function(e){var t=e.locale;return""+(e.fullyQualified?i:"")+function(e){return e===u?""+f:""+f+e+"/"}(t)+m}}}},5936:(e,t,n)=>{"use strict";n.d(t,{S:()=>i});var r=n(7294),a=n(6550),o=n(9688);function i(e){var t=(0,a.TH)(),n=(0,o.D9)(t),i=(0,o.zX)(e);(0,r.useEffect)((function(){n&&t!==n&&i({location:t,previousLocation:n})}),[i,t,n])}},6668:(e,t,n)=>{"use strict";n.d(t,{L:()=>a});var r=n(2263);function a(){return(0,r.Z)().siteConfig.themeConfig}},8802:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e,t){var n=t.trailingSlash,r=t.baseUrl;if(e.startsWith("#"))return e;if(void 0===n)return e;var a,o=e.split(/[#?]/)[0],i="/"===o||o===r?o:(a=o,n?function(e){return e.endsWith("/")?e:e+"/"}(a):function(e){return e.endsWith("/")?e.slice(0,-1):e}(a));return e.replace(o,i)}},4143:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.getErrorCausalChain=void 0,t.getErrorCausalChain=function e(t){return t.cause?[t].concat(e(t.cause)):[t]}},8780:function(e,t,n){"use strict";var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.getErrorCausalChain=t.applyTrailingSlash=t.blogPostContainerID=void 0,t.blogPostContainerID="__blog-post-container";var a=n(8802);Object.defineProperty(t,"applyTrailingSlash",{enumerable:!0,get:function(){return r(a).default}});var o=n(4143);Object.defineProperty(t,"getErrorCausalChain",{enumerable:!0,get:function(){return o.getErrorCausalChain}})},6010:(e,t,n)=>{"use strict";function r(e){var t,n,a="";if("string"==typeof e||"number"==typeof e)a+=e;else if("object"==typeof e)if(Array.isArray(e))for(t=0;ta});const a=function(){for(var e,t,n=0,a="";n{"use strict";n.d(t,{lX:()=>k,q_:()=>A,ob:()=>h,PP:()=>R,Ep:()=>m,Hp:()=>g});var r=n(7462);function a(e){return"/"===e.charAt(0)}function o(e,t){for(var n=t,r=n+1,a=e.length;r=0;d--){var f=i[d];"."===f?o(i,d):".."===f?(o(i,d),p++):p&&(o(i,d),p--)}if(!u)for(;p--;p)i.unshift("..");!u||""===i[0]||i[0]&&a(i[0])||i.unshift("");var m=i.join("/");return n&&"/"!==m.substr(-1)&&(m+="/"),m};function l(e){return e.valueOf?e.valueOf():Object.prototype.valueOf.call(e)}const s=function e(t,n){if(t===n)return!0;if(null==t||null==n)return!1;if(Array.isArray(t))return Array.isArray(n)&&t.length===n.length&&t.every((function(t,r){return e(t,n[r])}));if("object"==typeof t||"object"==typeof n){var r=l(t),a=l(n);return r!==t||a!==n?e(r,a):Object.keys(Object.assign({},t,n)).every((function(r){return e(t[r],n[r])}))}return!1};var u=n(8776);function c(e){return"/"===e.charAt(0)?e:"/"+e}function p(e){return"/"===e.charAt(0)?e.substr(1):e}function d(e,t){return function(e,t){return 0===e.toLowerCase().indexOf(t.toLowerCase())&&-1!=="/?#".indexOf(e.charAt(t.length))}(e,t)?e.substr(t.length):e}function f(e){return"/"===e.charAt(e.length-1)?e.slice(0,-1):e}function m(e){var t=e.pathname,n=e.search,r=e.hash,a=t||"/";return n&&"?"!==n&&(a+="?"===n.charAt(0)?n:"?"+n),r&&"#"!==r&&(a+="#"===r.charAt(0)?r:"#"+r),a}function h(e,t,n,a){var o;"string"==typeof e?(o=function(e){var t=e||"/",n="",r="",a=t.indexOf("#");-1!==a&&(r=t.substr(a),t=t.substr(0,a));var o=t.indexOf("?");return-1!==o&&(n=t.substr(o),t=t.substr(0,o)),{pathname:t,search:"?"===n?"":n,hash:"#"===r?"":r}}(e),o.state=t):(void 0===(o=(0,r.Z)({},e)).pathname&&(o.pathname=""),o.search?"?"!==o.search.charAt(0)&&(o.search="?"+o.search):o.search="",o.hash?"#"!==o.hash.charAt(0)&&(o.hash="#"+o.hash):o.hash="",void 0!==t&&void 0===o.state&&(o.state=t));try{o.pathname=decodeURI(o.pathname)}catch(l){throw l instanceof URIError?new URIError('Pathname "'+o.pathname+'" could not be decoded. This is likely caused by an invalid percent-encoding.'):l}return n&&(o.key=n),a?o.pathname?"/"!==o.pathname.charAt(0)&&(o.pathname=i(o.pathname,a.pathname)):o.pathname=a.pathname:o.pathname||(o.pathname="/"),o}function g(e,t){return e.pathname===t.pathname&&e.search===t.search&&e.hash===t.hash&&e.key===t.key&&s(e.state,t.state)}function b(){var e=null;var t=[];return{setPrompt:function(t){return e=t,function(){e===t&&(e=null)}},confirmTransitionTo:function(t,n,r,a){if(null!=e){var o="function"==typeof e?e(t,n):e;"string"==typeof o?"function"==typeof r?r(o,a):a(!0):a(!1!==o)}else a(!0)},appendListener:function(e){var n=!0;function r(){n&&e.apply(void 0,arguments)}return t.push(r),function(){n=!1,t=t.filter((function(e){return e!==r}))}},notifyListeners:function(){for(var e=arguments.length,n=new Array(e),r=0;rt?n.splice(t,n.length-t,a):n.push(a),p({action:r,location:a,index:t,entries:n})}}))},replace:function(e,t){var r="REPLACE",a=h(e,t,d(),w.location);c.confirmTransitionTo(a,r,n,(function(e){e&&(w.entries[w.index]=a,p({action:r,location:a}))}))},go:y,goBack:function(){y(-1)},goForward:function(){y(1)},canGo:function(e){var t=w.index+e;return t>=0&&t{"use strict";var r=n(9864),a={childContextTypes:!0,contextType:!0,contextTypes:!0,defaultProps:!0,displayName:!0,getDefaultProps:!0,getDerivedStateFromError:!0,getDerivedStateFromProps:!0,mixins:!0,propTypes:!0,type:!0},o={name:!0,length:!0,prototype:!0,caller:!0,callee:!0,arguments:!0,arity:!0},i={$$typeof:!0,compare:!0,defaultProps:!0,displayName:!0,propTypes:!0,type:!0},l={};function s(e){return r.isMemo(e)?i:l[e.$$typeof]||a}l[r.ForwardRef]={$$typeof:!0,render:!0,defaultProps:!0,displayName:!0,propTypes:!0},l[r.Memo]=i;var u=Object.defineProperty,c=Object.getOwnPropertyNames,p=Object.getOwnPropertySymbols,d=Object.getOwnPropertyDescriptor,f=Object.getPrototypeOf,m=Object.prototype;e.exports=function e(t,n,r){if("string"!=typeof n){if(m){var a=f(n);a&&a!==m&&e(t,a,r)}var i=c(n);p&&(i=i.concat(p(n)));for(var l=s(t),h=s(n),g=0;g{"use strict";e.exports=function(e,t,n,r,a,o,i,l){if(!e){var s;if(void 0===t)s=new Error("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.");else{var u=[n,r,a,o,i,l],c=0;(s=new Error(t.replace(/%s/g,(function(){return u[c++]})))).name="Invariant Violation"}throw s.framesToPop=1,s}}},2497:(e,t,n)=>{"use strict";n.r(t)},2295:(e,t,n)=>{"use strict";n.r(t)},4865:function(e,t,n){var r,a;r=function(){var e,t,n={version:"0.2.0"},r=n.settings={minimum:.08,easing:"ease",positionUsing:"",speed:200,trickle:!0,trickleRate:.02,trickleSpeed:800,showSpinner:!0,barSelector:'[role="bar"]',spinnerSelector:'[role="spinner"]',parent:"body",template:'
'};function a(e,t,n){return en?n:e}function o(e){return 100*(-1+e)}function i(e,t,n){var a;return(a="translate3d"===r.positionUsing?{transform:"translate3d("+o(e)+"%,0,0)"}:"translate"===r.positionUsing?{transform:"translate("+o(e)+"%,0)"}:{"margin-left":o(e)+"%"}).transition="all "+t+"ms "+n,a}n.configure=function(e){var t,n;for(t in e)void 0!==(n=e[t])&&e.hasOwnProperty(t)&&(r[t]=n);return this},n.status=null,n.set=function(e){var t=n.isStarted();e=a(e,r.minimum,1),n.status=1===e?null:e;var o=n.render(!t),u=o.querySelector(r.barSelector),c=r.speed,p=r.easing;return o.offsetWidth,l((function(t){""===r.positionUsing&&(r.positionUsing=n.getPositioningCSS()),s(u,i(e,c,p)),1===e?(s(o,{transition:"none",opacity:1}),o.offsetWidth,setTimeout((function(){s(o,{transition:"all "+c+"ms linear",opacity:0}),setTimeout((function(){n.remove(),t()}),c)}),c)):setTimeout(t,c)})),this},n.isStarted=function(){return"number"==typeof n.status},n.start=function(){n.status||n.set(0);var e=function(){setTimeout((function(){n.status&&(n.trickle(),e())}),r.trickleSpeed)};return r.trickle&&e(),this},n.done=function(e){return e||n.status?n.inc(.3+.5*Math.random()).set(1):this},n.inc=function(e){var t=n.status;return t?("number"!=typeof e&&(e=(1-t)*a(Math.random()*t,.1,.95)),t=a(t+e,0,.994),n.set(t)):n.start()},n.trickle=function(){return n.inc(Math.random()*r.trickleRate)},e=0,t=0,n.promise=function(r){return r&&"resolved"!==r.state()?(0===t&&n.start(),e++,t++,r.always((function(){0==--t?(e=0,n.done()):n.set((e-t)/e)})),this):this},n.render=function(e){if(n.isRendered())return document.getElementById("nprogress");c(document.documentElement,"nprogress-busy");var t=document.createElement("div");t.id="nprogress",t.innerHTML=r.template;var a,i=t.querySelector(r.barSelector),l=e?"-100":o(n.status||0),u=document.querySelector(r.parent);return s(i,{transition:"all 0 linear",transform:"translate3d("+l+"%,0,0)"}),r.showSpinner||(a=t.querySelector(r.spinnerSelector))&&f(a),u!=document.body&&c(u,"nprogress-custom-parent"),u.appendChild(t),t},n.remove=function(){p(document.documentElement,"nprogress-busy"),p(document.querySelector(r.parent),"nprogress-custom-parent");var e=document.getElementById("nprogress");e&&f(e)},n.isRendered=function(){return!!document.getElementById("nprogress")},n.getPositioningCSS=function(){var e=document.body.style,t="WebkitTransform"in e?"Webkit":"MozTransform"in e?"Moz":"msTransform"in e?"ms":"OTransform"in e?"O":"";return t+"Perspective"in e?"translate3d":t+"Transform"in e?"translate":"margin"};var l=function(){var e=[];function t(){var n=e.shift();n&&n(t)}return function(n){e.push(n),1==e.length&&t()}}(),s=function(){var e=["Webkit","O","Moz","ms"],t={};function n(e){return e.replace(/^-ms-/,"ms-").replace(/-([\da-z])/gi,(function(e,t){return t.toUpperCase()}))}function r(t){var n=document.body.style;if(t in n)return t;for(var r,a=e.length,o=t.charAt(0).toUpperCase()+t.slice(1);a--;)if((r=e[a]+o)in n)return r;return t}function a(e){return e=n(e),t[e]||(t[e]=r(e))}function o(e,t,n){t=a(t),e.style[t]=n}return function(e,t){var n,r,a=arguments;if(2==a.length)for(n in t)void 0!==(r=t[n])&&t.hasOwnProperty(n)&&o(e,n,r);else o(e,a[1],a[2])}}();function u(e,t){return("string"==typeof e?e:d(e)).indexOf(" "+t+" ")>=0}function c(e,t){var n=d(e),r=n+t;u(n,t)||(e.className=r.substring(1))}function p(e,t){var n,r=d(e);u(e,t)&&(n=r.replace(" "+t+" "," "),e.className=n.substring(1,n.length-1))}function d(e){return(" "+(e.className||"")+" ").replace(/\s+/gi," ")}function f(e){e&&e.parentNode&&e.parentNode.removeChild(e)}return n},void 0===(a="function"==typeof r?r.call(t,n,t,e):r)||(e.exports=a)},7418:e=>{"use strict";var t=Object.getOwnPropertySymbols,n=Object.prototype.hasOwnProperty,r=Object.prototype.propertyIsEnumerable;e.exports=function(){try{if(!Object.assign)return!1;var e=new String("abc");if(e[5]="de","5"===Object.getOwnPropertyNames(e)[0])return!1;for(var t={},n=0;n<10;n++)t["_"+String.fromCharCode(n)]=n;if("0123456789"!==Object.getOwnPropertyNames(t).map((function(e){return t[e]})).join(""))return!1;var r={};return"abcdefghijklmnopqrst".split("").forEach((function(e){r[e]=e})),"abcdefghijklmnopqrst"===Object.keys(Object.assign({},r)).join("")}catch(a){return!1}}()?Object.assign:function(e,a){for(var o,i,l=function(e){if(null==e)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(e)}(e),s=1;s{"use strict";n.d(t,{Z:()=>o});var r=function(){var e=/(?:^|\s)lang(?:uage)?-([\w-]+)(?=\s|$)/i,t=0,n={},r={util:{encode:function e(t){return t instanceof a?new a(t.type,e(t.content),t.alias):Array.isArray(t)?t.map(e):t.replace(/&/g,"&").replace(/=p.reach);k+=S.value.length,S=S.next){var E=S.value;if(t.length>e.length)return;if(!(E instanceof a)){var C,x=1;if(v){if(!(C=o(_,k,e,b))||C.index>=e.length)break;var T=C.index,P=C.index+C[0].length,A=k;for(A+=S.value.length;T>=A;)A+=(S=S.next).value.length;if(k=A-=S.value.length,S.value instanceof a)continue;for(var L=S;L!==t.tail&&(Ap.reach&&(p.reach=N);var M=S.prev;if(O&&(M=s(t,M,O),k+=O.length),u(t,M,x),S=s(t,M,new a(d,g?r.tokenize(R,g):R,y,R)),I&&s(t,S,I),x>1){var D={cause:d+","+m,reach:N};i(e,t,n,S.prev,k,D),p&&D.reach>p.reach&&(p.reach=D.reach)}}}}}}function l(){var e={value:null,prev:null,next:null},t={value:null,prev:e,next:null};e.next=t,this.head=e,this.tail=t,this.length=0}function s(e,t,n){var r=t.next,a={value:n,prev:t,next:r};return t.next=a,r.prev=a,e.length++,a}function u(e,t,n){for(var r=t.next,a=0;a"+o.content+""},r}(),a=r;r.default=r,a.languages.markup={comment:{pattern://,greedy:!0},prolog:{pattern:/<\?[\s\S]+?\?>/,greedy:!0},doctype:{pattern:/"'[\]]|"[^"]*"|'[^']*')+(?:\[(?:[^<"'\]]|"[^"]*"|'[^']*'|<(?!!--)|)*\]\s*)?>/i,greedy:!0,inside:{"internal-subset":{pattern:/(^[^\[]*\[)[\s\S]+(?=\]>$)/,lookbehind:!0,greedy:!0,inside:null},string:{pattern:/"[^"]*"|'[^']*'/,greedy:!0},punctuation:/^$|[[\]]/,"doctype-tag":/^DOCTYPE/i,name:/[^\s<>'"]+/}},cdata:{pattern://i,greedy:!0},tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"special-attr":[],"attr-value":{pattern:/=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/,inside:{punctuation:[{pattern:/^=/,alias:"attr-equals"},/"|'/]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:[{pattern:/&[\da-z]{1,8};/i,alias:"named-entity"},/&#x?[\da-f]{1,8};/i]},a.languages.markup.tag.inside["attr-value"].inside.entity=a.languages.markup.entity,a.languages.markup.doctype.inside["internal-subset"].inside=a.languages.markup,a.hooks.add("wrap",(function(e){"entity"===e.type&&(e.attributes.title=e.content.replace(/&/,"&"))})),Object.defineProperty(a.languages.markup.tag,"addInlined",{value:function(e,t){var n={};n["language-"+t]={pattern:/(^$)/i,lookbehind:!0,inside:a.languages[t]},n.cdata=/^$/i;var r={"included-cdata":{pattern://i,inside:n}};r["language-"+t]={pattern:/[\s\S]+/,inside:a.languages[t]};var o={};o[e]={pattern:RegExp(/(<__[^>]*>)(?:))*\]\]>|(?!)/.source.replace(/__/g,(function(){return e})),"i"),lookbehind:!0,greedy:!0,inside:r},a.languages.insertBefore("markup","cdata",o)}}),Object.defineProperty(a.languages.markup.tag,"addAttribute",{value:function(e,t){a.languages.markup.tag.inside["special-attr"].push({pattern:RegExp(/(^|["'\s])/.source+"(?:"+e+")"+/\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))/.source,"i"),lookbehind:!0,inside:{"attr-name":/^[^\s=]+/,"attr-value":{pattern:/=[\s\S]+/,inside:{value:{pattern:/(^=\s*(["']|(?!["'])))\S[\s\S]*(?=\2$)/,lookbehind:!0,alias:[t,"language-"+t],inside:a.languages[t]},punctuation:[{pattern:/^=/,alias:"attr-equals"},/"|'/]}}}})}}),a.languages.html=a.languages.markup,a.languages.mathml=a.languages.markup,a.languages.svg=a.languages.markup,a.languages.xml=a.languages.extend("markup",{}),a.languages.ssml=a.languages.xml,a.languages.atom=a.languages.xml,a.languages.rss=a.languages.xml,function(e){var t="\\b(?:BASH|BASHOPTS|BASH_ALIASES|BASH_ARGC|BASH_ARGV|BASH_CMDS|BASH_COMPLETION_COMPAT_DIR|BASH_LINENO|BASH_REMATCH|BASH_SOURCE|BASH_VERSINFO|BASH_VERSION|COLORTERM|COLUMNS|COMP_WORDBREAKS|DBUS_SESSION_BUS_ADDRESS|DEFAULTS_PATH|DESKTOP_SESSION|DIRSTACK|DISPLAY|EUID|GDMSESSION|GDM_LANG|GNOME_KEYRING_CONTROL|GNOME_KEYRING_PID|GPG_AGENT_INFO|GROUPS|HISTCONTROL|HISTFILE|HISTFILESIZE|HISTSIZE|HOME|HOSTNAME|HOSTTYPE|IFS|INSTANCE|JOB|LANG|LANGUAGE|LC_ADDRESS|LC_ALL|LC_IDENTIFICATION|LC_MEASUREMENT|LC_MONETARY|LC_NAME|LC_NUMERIC|LC_PAPER|LC_TELEPHONE|LC_TIME|LESSCLOSE|LESSOPEN|LINES|LOGNAME|LS_COLORS|MACHTYPE|MAILCHECK|MANDATORY_PATH|NO_AT_BRIDGE|OLDPWD|OPTERR|OPTIND|ORBIT_SOCKETDIR|OSTYPE|PAPERSIZE|PATH|PIPESTATUS|PPID|PS1|PS2|PS3|PS4|PWD|RANDOM|REPLY|SECONDS|SELINUX_INIT|SESSION|SESSIONTYPE|SESSION_MANAGER|SHELL|SHELLOPTS|SHLVL|SSH_AUTH_SOCK|TERM|UID|UPSTART_EVENTS|UPSTART_INSTANCE|UPSTART_JOB|UPSTART_SESSION|USER|WINDOWID|XAUTHORITY|XDG_CONFIG_DIRS|XDG_CURRENT_DESKTOP|XDG_DATA_DIRS|XDG_GREETER_DATA_DIR|XDG_MENU_PREFIX|XDG_RUNTIME_DIR|XDG_SEAT|XDG_SEAT_PATH|XDG_SESSION_DESKTOP|XDG_SESSION_ID|XDG_SESSION_PATH|XDG_SESSION_TYPE|XDG_VTNR|XMODIFIERS)\\b",n={pattern:/(^(["']?)\w+\2)[ \t]+\S.*/,lookbehind:!0,alias:"punctuation",inside:null},r={bash:n,environment:{pattern:RegExp("\\$"+t),alias:"constant"},variable:[{pattern:/\$?\(\([\s\S]+?\)\)/,greedy:!0,inside:{variable:[{pattern:/(^\$\(\([\s\S]+)\)\)/,lookbehind:!0},/^\$\(\(/],number:/\b0x[\dA-Fa-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:[Ee]-?\d+)?/,operator:/--|\+\+|\*\*=?|<<=?|>>=?|&&|\|\||[=!+\-*/%<>^&|]=?|[?~:]/,punctuation:/\(\(?|\)\)?|,|;/}},{pattern:/\$\((?:\([^)]+\)|[^()])+\)|`[^`]+`/,greedy:!0,inside:{variable:/^\$\(|^`|\)$|`$/}},{pattern:/\$\{[^}]+\}/,greedy:!0,inside:{operator:/:[-=?+]?|[!\/]|##?|%%?|\^\^?|,,?/,punctuation:/[\[\]]/,environment:{pattern:RegExp("(\\{)"+t),lookbehind:!0,alias:"constant"}}},/\$(?:\w+|[#?*!@$])/],entity:/\\(?:[abceEfnrtv\\"]|O?[0-7]{1,3}|U[0-9a-fA-F]{8}|u[0-9a-fA-F]{4}|x[0-9a-fA-F]{1,2})/};e.languages.bash={shebang:{pattern:/^#!\s*\/.*/,alias:"important"},comment:{pattern:/(^|[^"{\\$])#.*/,lookbehind:!0},"function-name":[{pattern:/(\bfunction\s+)[\w-]+(?=(?:\s*\(?:\s*\))?\s*\{)/,lookbehind:!0,alias:"function"},{pattern:/\b[\w-]+(?=\s*\(\s*\)\s*\{)/,alias:"function"}],"for-or-select":{pattern:/(\b(?:for|select)\s+)\w+(?=\s+in\s)/,alias:"variable",lookbehind:!0},"assign-left":{pattern:/(^|[\s;|&]|[<>]\()\w+(?=\+?=)/,inside:{environment:{pattern:RegExp("(^|[\\s;|&]|[<>]\\()"+t),lookbehind:!0,alias:"constant"}},alias:"variable",lookbehind:!0},string:[{pattern:/((?:^|[^<])<<-?\s*)(\w+)\s[\s\S]*?(?:\r?\n|\r)\2/,lookbehind:!0,greedy:!0,inside:r},{pattern:/((?:^|[^<])<<-?\s*)(["'])(\w+)\2\s[\s\S]*?(?:\r?\n|\r)\3/,lookbehind:!0,greedy:!0,inside:{bash:n}},{pattern:/(^|[^\\](?:\\\\)*)"(?:\\[\s\S]|\$\([^)]+\)|\$(?!\()|`[^`]+`|[^"\\`$])*"/,lookbehind:!0,greedy:!0,inside:r},{pattern:/(^|[^$\\])'[^']*'/,lookbehind:!0,greedy:!0},{pattern:/\$'(?:[^'\\]|\\[\s\S])*'/,greedy:!0,inside:{entity:r.entity}}],environment:{pattern:RegExp("\\$?"+t),alias:"constant"},variable:r.variable,function:{pattern:/(^|[\s;|&]|[<>]\()(?:add|apropos|apt|apt-cache|apt-get|aptitude|aspell|automysqlbackup|awk|basename|bash|bc|bconsole|bg|bzip2|cal|cat|cfdisk|chgrp|chkconfig|chmod|chown|chroot|cksum|clear|cmp|column|comm|composer|cp|cron|crontab|csplit|curl|cut|date|dc|dd|ddrescue|debootstrap|df|diff|diff3|dig|dir|dircolors|dirname|dirs|dmesg|docker|docker-compose|du|egrep|eject|env|ethtool|expand|expect|expr|fdformat|fdisk|fg|fgrep|file|find|fmt|fold|format|free|fsck|ftp|fuser|gawk|git|gparted|grep|groupadd|groupdel|groupmod|groups|grub-mkconfig|gzip|halt|head|hg|history|host|hostname|htop|iconv|id|ifconfig|ifdown|ifup|import|install|ip|jobs|join|kill|killall|less|link|ln|locate|logname|logrotate|look|lpc|lpr|lprint|lprintd|lprintq|lprm|ls|lsof|lynx|make|man|mc|mdadm|mkconfig|mkdir|mke2fs|mkfifo|mkfs|mkisofs|mknod|mkswap|mmv|more|most|mount|mtools|mtr|mutt|mv|nano|nc|netstat|nice|nl|node|nohup|notify-send|npm|nslookup|op|open|parted|passwd|paste|pathchk|ping|pkill|pnpm|podman|podman-compose|popd|pr|printcap|printenv|ps|pushd|pv|quota|quotacheck|quotactl|ram|rar|rcp|reboot|remsync|rename|renice|rev|rm|rmdir|rpm|rsync|scp|screen|sdiff|sed|sendmail|seq|service|sftp|sh|shellcheck|shuf|shutdown|sleep|slocate|sort|split|ssh|stat|strace|su|sudo|sum|suspend|swapon|sync|tac|tail|tar|tee|time|timeout|top|touch|tr|traceroute|tsort|tty|umount|uname|unexpand|uniq|units|unrar|unshar|unzip|update-grub|uptime|useradd|userdel|usermod|users|uudecode|uuencode|v|vcpkg|vdir|vi|vim|virsh|vmstat|wait|watch|wc|wget|whereis|which|who|whoami|write|xargs|xdg-open|yarn|yes|zenity|zip|zsh|zypper)(?=$|[)\s;|&])/,lookbehind:!0},keyword:{pattern:/(^|[\s;|&]|[<>]\()(?:case|do|done|elif|else|esac|fi|for|function|if|in|select|then|until|while)(?=$|[)\s;|&])/,lookbehind:!0},builtin:{pattern:/(^|[\s;|&]|[<>]\()(?:\.|:|alias|bind|break|builtin|caller|cd|command|continue|declare|echo|enable|eval|exec|exit|export|getopts|hash|help|let|local|logout|mapfile|printf|pwd|read|readarray|readonly|return|set|shift|shopt|source|test|times|trap|type|typeset|ulimit|umask|unalias|unset)(?=$|[)\s;|&])/,lookbehind:!0,alias:"class-name"},boolean:{pattern:/(^|[\s;|&]|[<>]\()(?:false|true)(?=$|[)\s;|&])/,lookbehind:!0},"file-descriptor":{pattern:/\B&\d\b/,alias:"important"},operator:{pattern:/\d?<>|>\||\+=|=[=~]?|!=?|<<[<-]?|[&\d]?>>|\d[<>]&?|[<>][&=]?|&[>&]?|\|[&|]?/,inside:{"file-descriptor":{pattern:/^\d/,alias:"important"}}},punctuation:/\$?\(\(?|\)\)?|\.\.|[{}[\];\\]/,number:{pattern:/(^|\s)(?:[1-9]\d*|0)(?:[.,]\d+)?\b/,lookbehind:!0}},n.inside=e.languages.bash;for(var a=["comment","function-name","for-or-select","assign-left","string","environment","function","keyword","builtin","boolean","file-descriptor","operator","punctuation","number"],o=r.variable[1].inside,i=0;i]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/,punctuation:/[{}[\];(),.:]/},a.languages.c=a.languages.extend("clike",{comment:{pattern:/\/\/(?:[^\r\n\\]|\\(?:\r\n?|\n|(?![\r\n])))*|\/\*[\s\S]*?(?:\*\/|$)/,greedy:!0},string:{pattern:/"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"/,greedy:!0},"class-name":{pattern:/(\b(?:enum|struct)\s+(?:__attribute__\s*\(\([\s\S]*?\)\)\s*)?)\w+|\b[a-z]\w*_t\b/,lookbehind:!0},keyword:/\b(?:_Alignas|_Alignof|_Atomic|_Bool|_Complex|_Generic|_Imaginary|_Noreturn|_Static_assert|_Thread_local|__attribute__|asm|auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|inline|int|long|register|return|short|signed|sizeof|static|struct|switch|typedef|typeof|union|unsigned|void|volatile|while)\b/,function:/\b[a-z_]\w*(?=\s*\()/i,number:/(?:\b0x(?:[\da-f]+(?:\.[\da-f]*)?|\.[\da-f]+)(?:p[+-]?\d+)?|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?)[ful]{0,4}/i,operator:/>>=?|<<=?|->|([-+&|:])\1|[?:~]|[-+*/%&|^!=<>]=?/}),a.languages.insertBefore("c","string",{char:{pattern:/'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n]){0,32}'/,greedy:!0}}),a.languages.insertBefore("c","string",{macro:{pattern:/(^[\t ]*)#\s*[a-z](?:[^\r\n\\/]|\/(?!\*)|\/\*(?:[^*]|\*(?!\/))*\*\/|\\(?:\r\n|[\s\S]))*/im,lookbehind:!0,greedy:!0,alias:"property",inside:{string:[{pattern:/^(#\s*include\s*)<[^>]+>/,lookbehind:!0},a.languages.c.string],char:a.languages.c.char,comment:a.languages.c.comment,"macro-name":[{pattern:/(^#\s*define\s+)\w+\b(?!\()/i,lookbehind:!0},{pattern:/(^#\s*define\s+)\w+\b(?=\()/i,lookbehind:!0,alias:"function"}],directive:{pattern:/^(#\s*)[a-z]+/,lookbehind:!0,alias:"keyword"},"directive-hash":/^#/,punctuation:/##|\\(?=[\r\n])/,expression:{pattern:/\S[\s\S]*/,inside:a.languages.c}}}}),a.languages.insertBefore("c","function",{constant:/\b(?:EOF|NULL|SEEK_CUR|SEEK_END|SEEK_SET|__DATE__|__FILE__|__LINE__|__TIMESTAMP__|__TIME__|__func__|stderr|stdin|stdout)\b/}),delete a.languages.c.boolean,function(e){var t=/\b(?:alignas|alignof|asm|auto|bool|break|case|catch|char|char16_t|char32_t|char8_t|class|co_await|co_return|co_yield|compl|concept|const|const_cast|consteval|constexpr|constinit|continue|decltype|default|delete|do|double|dynamic_cast|else|enum|explicit|export|extern|final|float|for|friend|goto|if|import|inline|int|int16_t|int32_t|int64_t|int8_t|long|module|mutable|namespace|new|noexcept|nullptr|operator|override|private|protected|public|register|reinterpret_cast|requires|return|short|signed|sizeof|static|static_assert|static_cast|struct|switch|template|this|thread_local|throw|try|typedef|typeid|typename|uint16_t|uint32_t|uint64_t|uint8_t|union|unsigned|using|virtual|void|volatile|wchar_t|while)\b/,n=/\b(?!)\w+(?:\s*\.\s*\w+)*\b/.source.replace(//g,(function(){return t.source}));e.languages.cpp=e.languages.extend("c",{"class-name":[{pattern:RegExp(/(\b(?:class|concept|enum|struct|typename)\s+)(?!)\w+/.source.replace(//g,(function(){return t.source}))),lookbehind:!0},/\b[A-Z]\w*(?=\s*::\s*\w+\s*\()/,/\b[A-Z_]\w*(?=\s*::\s*~\w+\s*\()/i,/\b\w+(?=\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>\s*::\s*\w+\s*\()/],keyword:t,number:{pattern:/(?:\b0b[01']+|\b0x(?:[\da-f']+(?:\.[\da-f']*)?|\.[\da-f']+)(?:p[+-]?[\d']+)?|(?:\b[\d']+(?:\.[\d']*)?|\B\.[\d']+)(?:e[+-]?[\d']+)?)[ful]{0,4}/i,greedy:!0},operator:/>>=?|<<=?|->|--|\+\+|&&|\|\||[?:~]|<=>|[-+*/%&|^!=<>]=?|\b(?:and|and_eq|bitand|bitor|not|not_eq|or|or_eq|xor|xor_eq)\b/,boolean:/\b(?:false|true)\b/}),e.languages.insertBefore("cpp","string",{module:{pattern:RegExp(/(\b(?:import|module)\s+)/.source+"(?:"+/"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|<[^<>\r\n]*>/.source+"|"+/(?:\s*:\s*)?|:\s*/.source.replace(//g,(function(){return n}))+")"),lookbehind:!0,greedy:!0,inside:{string:/^[<"][\s\S]+/,operator:/:/,punctuation:/\./}},"raw-string":{pattern:/R"([^()\\ ]{0,16})\([\s\S]*?\)\1"/,alias:"string",greedy:!0}}),e.languages.insertBefore("cpp","keyword",{"generic-function":{pattern:/\b(?!operator\b)[a-z_]\w*\s*<(?:[^<>]|<[^<>]*>)*>(?=\s*\()/i,inside:{function:/^\w+/,generic:{pattern:/<[\s\S]+/,alias:"class-name",inside:e.languages.cpp}}}}),e.languages.insertBefore("cpp","operator",{"double-colon":{pattern:/::/,alias:"punctuation"}}),e.languages.insertBefore("cpp","class-name",{"base-clause":{pattern:/(\b(?:class|struct)\s+\w+\s*:\s*)[^;{}"'\s]+(?:\s+[^;{}"'\s]+)*(?=\s*[;{])/,lookbehind:!0,greedy:!0,inside:e.languages.extend("cpp",{})}}),e.languages.insertBefore("inside","double-colon",{"class-name":/\b[a-z_]\w*\b(?!\s*::)/i},e.languages.cpp["base-clause"])}(a),function(e){var t=/(?:"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n])*')/;e.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:/@[\w-](?:[^;{\s]|\s+(?![\s{]))*(?:;|(?=\s*\{))/,inside:{rule:/^@[\w-]+/,"selector-function-argument":{pattern:/(\bselector\s*\(\s*(?![\s)]))(?:[^()\s]|\s+(?![\s)])|\((?:[^()]|\([^()]*\))*\))+(?=\s*\))/,lookbehind:!0,alias:"selector"},keyword:{pattern:/(^|[^\w-])(?:and|not|only|or)(?![\w-])/,lookbehind:!0}}},url:{pattern:RegExp("\\burl\\((?:"+t.source+"|"+/(?:[^\\\r\n()"']|\\[\s\S])*/.source+")\\)","i"),greedy:!0,inside:{function:/^url/i,punctuation:/^\(|\)$/,string:{pattern:RegExp("^"+t.source+"$"),alias:"url"}}},selector:{pattern:RegExp("(^|[{}\\s])[^{}\\s](?:[^{};\"'\\s]|\\s+(?![\\s{])|"+t.source+")*(?=\\s*\\{)"),lookbehind:!0},string:{pattern:t,greedy:!0},property:{pattern:/(^|[^-\w\xA0-\uFFFF])(?!\s)[-_a-z\xA0-\uFFFF](?:(?!\s)[-\w\xA0-\uFFFF])*(?=\s*:)/i,lookbehind:!0},important:/!important\b/i,function:{pattern:/(^|[^-a-z0-9])[-a-z0-9]+(?=\()/i,lookbehind:!0},punctuation:/[(){};:,]/},e.languages.css.atrule.inside.rest=e.languages.css;var n=e.languages.markup;n&&(n.tag.addInlined("style","css"),n.tag.addAttribute("style","css"))}(a),function(e){var t,n=/("|')(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/;e.languages.css.selector={pattern:e.languages.css.selector.pattern,lookbehind:!0,inside:t={"pseudo-element":/:(?:after|before|first-letter|first-line|selection)|::[-\w]+/,"pseudo-class":/:[-\w]+/,class:/\.[-\w]+/,id:/#[-\w]+/,attribute:{pattern:RegExp("\\[(?:[^[\\]\"']|"+n.source+")*\\]"),greedy:!0,inside:{punctuation:/^\[|\]$/,"case-sensitivity":{pattern:/(\s)[si]$/i,lookbehind:!0,alias:"keyword"},namespace:{pattern:/^(\s*)(?:(?!\s)[-*\w\xA0-\uFFFF])*\|(?!=)/,lookbehind:!0,inside:{punctuation:/\|$/}},"attr-name":{pattern:/^(\s*)(?:(?!\s)[-\w\xA0-\uFFFF])+/,lookbehind:!0},"attr-value":[n,{pattern:/(=\s*)(?:(?!\s)[-\w\xA0-\uFFFF])+(?=\s*$)/,lookbehind:!0}],operator:/[|~*^$]?=/}},"n-th":[{pattern:/(\(\s*)[+-]?\d*[\dn](?:\s*[+-]\s*\d+)?(?=\s*\))/,lookbehind:!0,inside:{number:/[\dn]+/,operator:/[+-]/}},{pattern:/(\(\s*)(?:even|odd)(?=\s*\))/i,lookbehind:!0}],combinator:/>|\+|~|\|\|/,punctuation:/[(),]/}},e.languages.css.atrule.inside["selector-function-argument"].inside=t,e.languages.insertBefore("css","property",{variable:{pattern:/(^|[^-\w\xA0-\uFFFF])--(?!\s)[-_a-z\xA0-\uFFFF](?:(?!\s)[-\w\xA0-\uFFFF])*/i,lookbehind:!0}});var r={pattern:/(\b\d+)(?:%|[a-z]+(?![\w-]))/,lookbehind:!0},a={pattern:/(^|[^\w.-])-?(?:\d+(?:\.\d+)?|\.\d+)/,lookbehind:!0};e.languages.insertBefore("css","function",{operator:{pattern:/(\s)[+\-*\/](?=\s)/,lookbehind:!0},hexcode:{pattern:/\B#[\da-f]{3,8}\b/i,alias:"color"},color:[{pattern:/(^|[^\w-])(?:AliceBlue|AntiqueWhite|Aqua|Aquamarine|Azure|Beige|Bisque|Black|BlanchedAlmond|Blue|BlueViolet|Brown|BurlyWood|CadetBlue|Chartreuse|Chocolate|Coral|CornflowerBlue|Cornsilk|Crimson|Cyan|DarkBlue|DarkCyan|DarkGoldenRod|DarkGr[ae]y|DarkGreen|DarkKhaki|DarkMagenta|DarkOliveGreen|DarkOrange|DarkOrchid|DarkRed|DarkSalmon|DarkSeaGreen|DarkSlateBlue|DarkSlateGr[ae]y|DarkTurquoise|DarkViolet|DeepPink|DeepSkyBlue|DimGr[ae]y|DodgerBlue|FireBrick|FloralWhite|ForestGreen|Fuchsia|Gainsboro|GhostWhite|Gold|GoldenRod|Gr[ae]y|Green|GreenYellow|HoneyDew|HotPink|IndianRed|Indigo|Ivory|Khaki|Lavender|LavenderBlush|LawnGreen|LemonChiffon|LightBlue|LightCoral|LightCyan|LightGoldenRodYellow|LightGr[ae]y|LightGreen|LightPink|LightSalmon|LightSeaGreen|LightSkyBlue|LightSlateGr[ae]y|LightSteelBlue|LightYellow|Lime|LimeGreen|Linen|Magenta|Maroon|MediumAquaMarine|MediumBlue|MediumOrchid|MediumPurple|MediumSeaGreen|MediumSlateBlue|MediumSpringGreen|MediumTurquoise|MediumVioletRed|MidnightBlue|MintCream|MistyRose|Moccasin|NavajoWhite|Navy|OldLace|Olive|OliveDrab|Orange|OrangeRed|Orchid|PaleGoldenRod|PaleGreen|PaleTurquoise|PaleVioletRed|PapayaWhip|PeachPuff|Peru|Pink|Plum|PowderBlue|Purple|Red|RosyBrown|RoyalBlue|SaddleBrown|Salmon|SandyBrown|SeaGreen|SeaShell|Sienna|Silver|SkyBlue|SlateBlue|SlateGr[ae]y|Snow|SpringGreen|SteelBlue|Tan|Teal|Thistle|Tomato|Transparent|Turquoise|Violet|Wheat|White|WhiteSmoke|Yellow|YellowGreen)(?![\w-])/i,lookbehind:!0},{pattern:/\b(?:hsl|rgb)\(\s*\d{1,3}\s*,\s*\d{1,3}%?\s*,\s*\d{1,3}%?\s*\)\B|\b(?:hsl|rgb)a\(\s*\d{1,3}\s*,\s*\d{1,3}%?\s*,\s*\d{1,3}%?\s*,\s*(?:0|0?\.\d+|1)\s*\)\B/i,inside:{unit:r,number:a,function:/[\w-]+(?=\()/,punctuation:/[(),]/}}],entity:/\\[\da-f]{1,8}/i,unit:r,number:a})}(a),a.languages.javascript=a.languages.extend("clike",{"class-name":[a.languages.clike["class-name"],{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$A-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\.(?:constructor|prototype))/,lookbehind:!0}],keyword:[{pattern:/((?:^|\})\s*)catch\b/,lookbehind:!0},{pattern:/(^|[^.]|\.\.\.\s*)\b(?:as|assert(?=\s*\{)|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally(?=\s*(?:\{|$))|for|from(?=\s*(?:['"]|$))|function|(?:get|set)(?=\s*(?:[#\[$\w\xA0-\uFFFF]|$))|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,lookbehind:!0}],function:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,number:{pattern:RegExp(/(^|[^\w$])/.source+"(?:"+/NaN|Infinity/.source+"|"+/0[bB][01]+(?:_[01]+)*n?/.source+"|"+/0[oO][0-7]+(?:_[0-7]+)*n?/.source+"|"+/0[xX][\dA-Fa-f]+(?:_[\dA-Fa-f]+)*n?/.source+"|"+/\d+(?:_\d+)*n/.source+"|"+/(?:\d+(?:_\d+)*(?:\.(?:\d+(?:_\d+)*)?)?|\.\d+(?:_\d+)*)(?:[Ee][+-]?\d+(?:_\d+)*)?/.source+")"+/(?![\w$])/.source),lookbehind:!0},operator:/--|\+\+|\*\*=?|=>|&&=?|\|\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\.{3}|\?\?=?|\?\.?|[~:]/}),a.languages.javascript["class-name"][0].pattern=/(\b(?:class|extends|implements|instanceof|interface|new)\s+)[\w.\\]+/,a.languages.insertBefore("javascript","keyword",{regex:{pattern:/((?:^|[^$\w\xA0-\uFFFF."'\])\s]|\b(?:return|yield))\s*)\/(?:\[(?:[^\]\\\r\n]|\\.)*\]|\\.|[^/\\\[\r\n])+\/[dgimyus]{0,7}(?=(?:\s|\/\*(?:[^*]|\*(?!\/))*\*\/)*(?:$|[\r\n,.;:})\]]|\/\/))/,lookbehind:!0,greedy:!0,inside:{"regex-source":{pattern:/^(\/)[\s\S]+(?=\/[a-z]*$)/,lookbehind:!0,alias:"language-regex",inside:a.languages.regex},"regex-delimiter":/^\/|\/$/,"regex-flags":/^[a-z]+$/}},"function-variable":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)\s*=>))/,alias:"function"},parameter:[{pattern:/(function(?:\s+(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)?\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\))/,lookbehind:!0,inside:a.languages.javascript},{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$a-z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*=>)/i,lookbehind:!0,inside:a.languages.javascript},{pattern:/(\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*=>)/,lookbehind:!0,inside:a.languages.javascript},{pattern:/((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*)\(\s*|\]\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*\{)/,lookbehind:!0,inside:a.languages.javascript}],constant:/\b[A-Z](?:[A-Z_]|\dx?)*\b/}),a.languages.insertBefore("javascript","string",{hashbang:{pattern:/^#!.*/,greedy:!0,alias:"comment"},"template-string":{pattern:/`(?:\\[\s\S]|\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}|(?!\$\{)[^\\`])*`/,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}/,lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:a.languages.javascript}},string:/[\s\S]+/}},"string-property":{pattern:/((?:^|[,{])[ \t]*)(["'])(?:\\(?:\r\n|[\s\S])|(?!\2)[^\\\r\n])*\2(?=\s*:)/m,lookbehind:!0,greedy:!0,alias:"property"}}),a.languages.insertBefore("javascript","operator",{"literal-property":{pattern:/((?:^|[,{])[ \t]*)(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*:)/m,lookbehind:!0,alias:"property"}}),a.languages.markup&&(a.languages.markup.tag.addInlined("script","javascript"),a.languages.markup.tag.addAttribute(/on(?:abort|blur|change|click|composition(?:end|start|update)|dblclick|error|focus(?:in|out)?|key(?:down|up)|load|mouse(?:down|enter|leave|move|out|over|up)|reset|resize|scroll|select|slotchange|submit|unload|wheel)/.source,"javascript")),a.languages.js=a.languages.javascript,function(e){var t=/#(?!\{).+/,n={pattern:/#\{[^}]+\}/,alias:"variable"};e.languages.coffeescript=e.languages.extend("javascript",{comment:t,string:[{pattern:/'(?:\\[\s\S]|[^\\'])*'/,greedy:!0},{pattern:/"(?:\\[\s\S]|[^\\"])*"/,greedy:!0,inside:{interpolation:n}}],keyword:/\b(?:and|break|by|catch|class|continue|debugger|delete|do|each|else|extend|extends|false|finally|for|if|in|instanceof|is|isnt|let|loop|namespace|new|no|not|null|of|off|on|or|own|return|super|switch|then|this|throw|true|try|typeof|undefined|unless|until|when|while|window|with|yes|yield)\b/,"class-member":{pattern:/@(?!\d)\w+/,alias:"variable"}}),e.languages.insertBefore("coffeescript","comment",{"multiline-comment":{pattern:/###[\s\S]+?###/,alias:"comment"},"block-regex":{pattern:/\/{3}[\s\S]*?\/{3}/,alias:"regex",inside:{comment:t,interpolation:n}}}),e.languages.insertBefore("coffeescript","string",{"inline-javascript":{pattern:/`(?:\\[\s\S]|[^\\`])*`/,inside:{delimiter:{pattern:/^`|`$/,alias:"punctuation"},script:{pattern:/[\s\S]+/,alias:"language-javascript",inside:e.languages.javascript}}},"multiline-string":[{pattern:/'''[\s\S]*?'''/,greedy:!0,alias:"string"},{pattern:/"""[\s\S]*?"""/,greedy:!0,alias:"string",inside:{interpolation:n}}]}),e.languages.insertBefore("coffeescript","keyword",{property:/(?!\d)\w+(?=\s*:(?!:))/}),delete e.languages.coffeescript["template-string"],e.languages.coffee=e.languages.coffeescript}(a),function(e){var t=/[*&][^\s[\]{},]+/,n=/!(?:<[\w\-%#;/?:@&=+$,.!~*'()[\]]+>|(?:[a-zA-Z\d-]*!)?[\w\-%#;/?:@&=+$.~*'()]+)?/,r="(?:"+n.source+"(?:[ \t]+"+t.source+")?|"+t.source+"(?:[ \t]+"+n.source+")?)",a=/(?:[^\s\x00-\x08\x0e-\x1f!"#%&'*,\-:>?@[\]`{|}\x7f-\x84\x86-\x9f\ud800-\udfff\ufffe\uffff]|[?:-])(?:[ \t]*(?:(?![#:])|:))*/.source.replace(//g,(function(){return/[^\s\x00-\x08\x0e-\x1f,[\]{}\x7f-\x84\x86-\x9f\ud800-\udfff\ufffe\uffff]/.source})),o=/"(?:[^"\\\r\n]|\\.)*"|'(?:[^'\\\r\n]|\\.)*'/.source;function i(e,t){t=(t||"").replace(/m/g,"")+"m";var n=/([:\-,[{]\s*(?:\s<>[ \t]+)?)(?:<>)(?=[ \t]*(?:$|,|\]|\}|(?:[\r\n]\s*)?#))/.source.replace(/<>/g,(function(){return r})).replace(/<>/g,(function(){return e}));return RegExp(n,t)}e.languages.yaml={scalar:{pattern:RegExp(/([\-:]\s*(?:\s<>[ \t]+)?[|>])[ \t]*(?:((?:\r?\n|\r)[ \t]+)\S[^\r\n]*(?:\2[^\r\n]+)*)/.source.replace(/<>/g,(function(){return r}))),lookbehind:!0,alias:"string"},comment:/#.*/,key:{pattern:RegExp(/((?:^|[:\-,[{\r\n?])[ \t]*(?:<>[ \t]+)?)<>(?=\s*:\s)/.source.replace(/<>/g,(function(){return r})).replace(/<>/g,(function(){return"(?:"+a+"|"+o+")"}))),lookbehind:!0,greedy:!0,alias:"atrule"},directive:{pattern:/(^[ \t]*)%.+/m,lookbehind:!0,alias:"important"},datetime:{pattern:i(/\d{4}-\d\d?-\d\d?(?:[tT]|[ \t]+)\d\d?:\d{2}:\d{2}(?:\.\d*)?(?:[ \t]*(?:Z|[-+]\d\d?(?::\d{2})?))?|\d{4}-\d{2}-\d{2}|\d\d?:\d{2}(?::\d{2}(?:\.\d*)?)?/.source),lookbehind:!0,alias:"number"},boolean:{pattern:i(/false|true/.source,"i"),lookbehind:!0,alias:"important"},null:{pattern:i(/null|~/.source,"i"),lookbehind:!0,alias:"important"},string:{pattern:i(o),lookbehind:!0,greedy:!0},number:{pattern:i(/[+-]?(?:0x[\da-f]+|0o[0-7]+|(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?|\.inf|\.nan)/.source,"i"),lookbehind:!0},tag:n,important:t,punctuation:/---|[:[\]{}\-,|>?]|\.\.\./},e.languages.yml=e.languages.yaml}(a),function(e){var t=/(?:\\.|[^\\\n\r]|(?:\n|\r\n?)(?![\r\n]))/.source;function n(e){return e=e.replace(//g,(function(){return t})),RegExp(/((?:^|[^\\])(?:\\{2})*)/.source+"(?:"+e+")")}var r=/(?:\\.|``(?:[^`\r\n]|`(?!`))+``|`[^`\r\n]+`|[^\\|\r\n`])+/.source,a=/\|?__(?:\|__)+\|?(?:(?:\n|\r\n?)|(?![\s\S]))/.source.replace(/__/g,(function(){return r})),o=/\|?[ \t]*:?-{3,}:?[ \t]*(?:\|[ \t]*:?-{3,}:?[ \t]*)+\|?(?:\n|\r\n?)/.source;e.languages.markdown=e.languages.extend("markup",{}),e.languages.insertBefore("markdown","prolog",{"front-matter-block":{pattern:/(^(?:\s*[\r\n])?)---(?!.)[\s\S]*?[\r\n]---(?!.)/,lookbehind:!0,greedy:!0,inside:{punctuation:/^---|---$/,"front-matter":{pattern:/\S+(?:\s+\S+)*/,alias:["yaml","language-yaml"],inside:e.languages.yaml}}},blockquote:{pattern:/^>(?:[\t ]*>)*/m,alias:"punctuation"},table:{pattern:RegExp("^"+a+o+"(?:"+a+")*","m"),inside:{"table-data-rows":{pattern:RegExp("^("+a+o+")(?:"+a+")*$"),lookbehind:!0,inside:{"table-data":{pattern:RegExp(r),inside:e.languages.markdown},punctuation:/\|/}},"table-line":{pattern:RegExp("^("+a+")"+o+"$"),lookbehind:!0,inside:{punctuation:/\||:?-{3,}:?/}},"table-header-row":{pattern:RegExp("^"+a+"$"),inside:{"table-header":{pattern:RegExp(r),alias:"important",inside:e.languages.markdown},punctuation:/\|/}}}},code:[{pattern:/((?:^|\n)[ \t]*\n|(?:^|\r\n?)[ \t]*\r\n?)(?: {4}|\t).+(?:(?:\n|\r\n?)(?: {4}|\t).+)*/,lookbehind:!0,alias:"keyword"},{pattern:/^```[\s\S]*?^```$/m,greedy:!0,inside:{"code-block":{pattern:/^(```.*(?:\n|\r\n?))[\s\S]+?(?=(?:\n|\r\n?)^```$)/m,lookbehind:!0},"code-language":{pattern:/^(```).+/,lookbehind:!0},punctuation:/```/}}],title:[{pattern:/\S.*(?:\n|\r\n?)(?:==+|--+)(?=[ \t]*$)/m,alias:"important",inside:{punctuation:/==+$|--+$/}},{pattern:/(^\s*)#.+/m,lookbehind:!0,alias:"important",inside:{punctuation:/^#+|#+$/}}],hr:{pattern:/(^\s*)([*-])(?:[\t ]*\2){2,}(?=\s*$)/m,lookbehind:!0,alias:"punctuation"},list:{pattern:/(^\s*)(?:[*+-]|\d+\.)(?=[\t ].)/m,lookbehind:!0,alias:"punctuation"},"url-reference":{pattern:/!?\[[^\]]+\]:[\t ]+(?:\S+|<(?:\\.|[^>\\])+>)(?:[\t ]+(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\)))?/,inside:{variable:{pattern:/^(!?\[)[^\]]+/,lookbehind:!0},string:/(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\))$/,punctuation:/^[\[\]!:]|[<>]/},alias:"url"},bold:{pattern:n(/\b__(?:(?!_)|_(?:(?!_))+_)+__\b|\*\*(?:(?!\*)|\*(?:(?!\*))+\*)+\*\*/.source),lookbehind:!0,greedy:!0,inside:{content:{pattern:/(^..)[\s\S]+(?=..$)/,lookbehind:!0,inside:{}},punctuation:/\*\*|__/}},italic:{pattern:n(/\b_(?:(?!_)|__(?:(?!_))+__)+_\b|\*(?:(?!\*)|\*\*(?:(?!\*))+\*\*)+\*/.source),lookbehind:!0,greedy:!0,inside:{content:{pattern:/(^.)[\s\S]+(?=.$)/,lookbehind:!0,inside:{}},punctuation:/[*_]/}},strike:{pattern:n(/(~~?)(?:(?!~))+\2/.source),lookbehind:!0,greedy:!0,inside:{content:{pattern:/(^~~?)[\s\S]+(?=\1$)/,lookbehind:!0,inside:{}},punctuation:/~~?/}},"code-snippet":{pattern:/(^|[^\\`])(?:``[^`\r\n]+(?:`[^`\r\n]+)*``(?!`)|`[^`\r\n]+`(?!`))/,lookbehind:!0,greedy:!0,alias:["code","keyword"]},url:{pattern:n(/!?\[(?:(?!\]))+\](?:\([^\s)]+(?:[\t ]+"(?:\\.|[^"\\])*")?\)|[ \t]?\[(?:(?!\]))+\])/.source),lookbehind:!0,greedy:!0,inside:{operator:/^!/,content:{pattern:/(^\[)[^\]]+(?=\])/,lookbehind:!0,inside:{}},variable:{pattern:/(^\][ \t]?\[)[^\]]+(?=\]$)/,lookbehind:!0},url:{pattern:/(^\]\()[^\s)]+/,lookbehind:!0},string:{pattern:/(^[ \t]+)"(?:\\.|[^"\\])*"(?=\)$)/,lookbehind:!0}}}}),["url","bold","italic","strike"].forEach((function(t){["url","bold","italic","strike","code-snippet"].forEach((function(n){t!==n&&(e.languages.markdown[t].inside.content.inside[n]=e.languages.markdown[n])}))})),e.hooks.add("after-tokenize",(function(e){"markdown"!==e.language&&"md"!==e.language||function e(t){if(t&&"string"!=typeof t)for(var n=0,r=t.length;n",quot:'"'},s=String.fromCodePoint||String.fromCharCode;e.languages.md=e.languages.markdown}(a),a.languages.graphql={comment:/#.*/,description:{pattern:/(?:"""(?:[^"]|(?!""")")*"""|"(?:\\.|[^\\"\r\n])*")(?=\s*[a-z_])/i,greedy:!0,alias:"string",inside:{"language-markdown":{pattern:/(^"(?:"")?)(?!\1)[\s\S]+(?=\1$)/,lookbehind:!0,inside:a.languages.markdown}}},string:{pattern:/"""(?:[^"]|(?!""")")*"""|"(?:\\.|[^\\"\r\n])*"/,greedy:!0},number:/(?:\B-|\b)\d+(?:\.\d+)?(?:e[+-]?\d+)?\b/i,boolean:/\b(?:false|true)\b/,variable:/\$[a-z_]\w*/i,directive:{pattern:/@[a-z_]\w*/i,alias:"function"},"attr-name":{pattern:/\b[a-z_]\w*(?=\s*(?:\((?:[^()"]|"(?:\\.|[^\\"\r\n])*")*\))?:)/i,greedy:!0},"atom-input":{pattern:/\b[A-Z]\w*Input\b/,alias:"class-name"},scalar:/\b(?:Boolean|Float|ID|Int|String)\b/,constant:/\b[A-Z][A-Z_\d]*\b/,"class-name":{pattern:/(\b(?:enum|implements|interface|on|scalar|type|union)\s+|&\s*|:\s*|\[)[A-Z_]\w*/,lookbehind:!0},fragment:{pattern:/(\bfragment\s+|\.{3}\s*(?!on\b))[a-zA-Z_]\w*/,lookbehind:!0,alias:"function"},"definition-mutation":{pattern:/(\bmutation\s+)[a-zA-Z_]\w*/,lookbehind:!0,alias:"function"},"definition-query":{pattern:/(\bquery\s+)[a-zA-Z_]\w*/,lookbehind:!0,alias:"function"},keyword:/\b(?:directive|enum|extend|fragment|implements|input|interface|mutation|on|query|repeatable|scalar|schema|subscription|type|union)\b/,operator:/[!=|&]|\.{3}/,"property-query":/\w+(?=\s*\()/,object:/\w+(?=\s*\{)/,punctuation:/[!(){}\[\]:=,]/,property:/\w+/},a.hooks.add("after-tokenize",(function(e){if("graphql"===e.language)for(var t=e.tokens.filter((function(e){return"string"!=typeof e&&"comment"!==e.type&&"scalar"!==e.type})),n=0;n0)){var l=d(/^\{$/,/^\}$/);if(-1===l)continue;for(var s=n;s=0&&f(u,"variable-input")}}}}function c(e){return t[n+e]}function p(e,t){t=t||0;for(var n=0;n?|<|>)?|>[>=]?|\b(?:AND|BETWEEN|DIV|ILIKE|IN|IS|LIKE|NOT|OR|REGEXP|RLIKE|SOUNDS LIKE|XOR)\b/i,punctuation:/[;[\]()`,.]/},function(e){var t=e.languages.javascript["template-string"],n=t.pattern.source,r=t.inside.interpolation,a=r.inside["interpolation-punctuation"],o=r.pattern.source;function i(t,r){if(e.languages[t])return{pattern:RegExp("((?:"+r+")\\s*)"+n),lookbehind:!0,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},"embedded-code":{pattern:/[\s\S]+/,alias:t}}}}function l(e,t){return"___"+t.toUpperCase()+"_"+e+"___"}function s(t,n,r){var a={code:t,grammar:n,language:r};return e.hooks.run("before-tokenize",a),a.tokens=e.tokenize(a.code,a.grammar),e.hooks.run("after-tokenize",a),a.tokens}function u(t){var n={};n["interpolation-punctuation"]=a;var o=e.tokenize(t,n);if(3===o.length){var i=[1,1];i.push.apply(i,s(o[1],e.languages.javascript,"javascript")),o.splice.apply(o,i)}return new e.Token("interpolation",o,r.alias,t)}function c(t,n,r){var a=e.tokenize(t,{interpolation:{pattern:RegExp(o),lookbehind:!0}}),i=0,c={},p=s(a.map((function(e){if("string"==typeof e)return e;for(var n,a=e.content;-1!==t.indexOf(n=l(i++,r)););return c[n]=a,n})).join(""),n,r),d=Object.keys(c);return i=0,function e(t){for(var n=0;n=d.length)return;var r=t[n];if("string"==typeof r||"string"==typeof r.content){var a=d[i],o="string"==typeof r?r:r.content,l=o.indexOf(a);if(-1!==l){++i;var s=o.substring(0,l),p=u(c[a]),f=o.substring(l+a.length),m=[];if(s&&m.push(s),m.push(p),f){var h=[f];e(h),m.push.apply(m,h)}"string"==typeof r?(t.splice.apply(t,[n,1].concat(m)),n+=m.length-1):r.content=m}}else{var g=r.content;Array.isArray(g)?e(g):e([g])}}}(p),new e.Token(r,p,"language-"+r,t)}e.languages.javascript["template-string"]=[i("css",/\b(?:styled(?:\([^)]*\))?(?:\s*\.\s*\w+(?:\([^)]*\))*)*|css(?:\s*\.\s*(?:global|resolve))?|createGlobalStyle|keyframes)/.source),i("html",/\bhtml|\.\s*(?:inner|outer)HTML\s*\+?=/.source),i("svg",/\bsvg/.source),i("markdown",/\b(?:markdown|md)/.source),i("graphql",/\b(?:gql|graphql(?:\s*\.\s*experimental)?)/.source),i("sql",/\bsql/.source),t].filter(Boolean);var p={javascript:!0,js:!0,typescript:!0,ts:!0,jsx:!0,tsx:!0};function d(e){return"string"==typeof e?e:Array.isArray(e)?e.map(d).join(""):d(e.content)}e.hooks.add("after-tokenize",(function(t){t.language in p&&function t(n){for(var r=0,a=n.length;r]|<(?:[^<>]|<[^<>]*>)*>)*>)?/,lookbehind:!0,greedy:!0,inside:null},builtin:/\b(?:Array|Function|Promise|any|boolean|console|never|number|string|symbol|unknown)\b/}),e.languages.typescript.keyword.push(/\b(?:abstract|declare|is|keyof|readonly|require)\b/,/\b(?:asserts|infer|interface|module|namespace|type)\b(?=\s*(?:[{_$a-zA-Z\xA0-\uFFFF]|$))/,/\btype\b(?=\s*(?:[\{*]|$))/),delete e.languages.typescript.parameter,delete e.languages.typescript["literal-property"];var t=e.languages.extend("typescript",{});delete t["class-name"],e.languages.typescript["class-name"].inside=t,e.languages.insertBefore("typescript","function",{decorator:{pattern:/@[$\w\xA0-\uFFFF]+/,inside:{at:{pattern:/^@/,alias:"operator"},function:/^[\s\S]+/}},"generic-function":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>(?=\s*\()/,greedy:!0,inside:{function:/^#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*/,generic:{pattern:/<[\s\S]+/,alias:"class-name",inside:t}}}}),e.languages.ts=e.languages.typescript}(a),function(e){function t(e,t){return RegExp(e.replace(//g,(function(){return/(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*/.source})),t)}e.languages.insertBefore("javascript","function-variable",{"method-variable":{pattern:RegExp("(\\.\\s*)"+e.languages.javascript["function-variable"].pattern.source),lookbehind:!0,alias:["function-variable","method","function","property-access"]}}),e.languages.insertBefore("javascript","function",{method:{pattern:RegExp("(\\.\\s*)"+e.languages.javascript.function.source),lookbehind:!0,alias:["function","property-access"]}}),e.languages.insertBefore("javascript","constant",{"known-class-name":[{pattern:/\b(?:(?:Float(?:32|64)|(?:Int|Uint)(?:8|16|32)|Uint8Clamped)?Array|ArrayBuffer|BigInt|Boolean|DataView|Date|Error|Function|Intl|JSON|(?:Weak)?(?:Map|Set)|Math|Number|Object|Promise|Proxy|Reflect|RegExp|String|Symbol|WebAssembly)\b/,alias:"class-name"},{pattern:/\b(?:[A-Z]\w*)Error\b/,alias:"class-name"}]}),e.languages.insertBefore("javascript","keyword",{imports:{pattern:t(/(\bimport\b\s*)(?:(?:\s*,\s*(?:\*\s*as\s+|\{[^{}]*\}))?|\*\s*as\s+|\{[^{}]*\})(?=\s*\bfrom\b)/.source),lookbehind:!0,inside:e.languages.javascript},exports:{pattern:t(/(\bexport\b\s*)(?:\*(?:\s*as\s+)?(?=\s*\bfrom\b)|\{[^{}]*\})/.source),lookbehind:!0,inside:e.languages.javascript}}),e.languages.javascript.keyword.unshift({pattern:/\b(?:as|default|export|from|import)\b/,alias:"module"},{pattern:/\b(?:await|break|catch|continue|do|else|finally|for|if|return|switch|throw|try|while|yield)\b/,alias:"control-flow"},{pattern:/\bnull\b/,alias:["null","nil"]},{pattern:/\bundefined\b/,alias:"nil"}),e.languages.insertBefore("javascript","operator",{spread:{pattern:/\.{3}/,alias:"operator"},arrow:{pattern:/=>/,alias:"operator"}}),e.languages.insertBefore("javascript","punctuation",{"property-access":{pattern:t(/(\.\s*)#?/.source),lookbehind:!0},"maybe-class-name":{pattern:/(^|[^$\w\xA0-\uFFFF])[A-Z][$\w\xA0-\uFFFF]+/,lookbehind:!0},dom:{pattern:/\b(?:document|(?:local|session)Storage|location|navigator|performance|window)\b/,alias:"variable"},console:{pattern:/\bconsole(?=\s*\.)/,alias:"class-name"}});for(var n=["function","function-variable","method","method-variable","property-access"],r=0;r*\.{3}(?:[^{}]|)*\})/.source;function o(e,t){return e=e.replace(//g,(function(){return n})).replace(//g,(function(){return r})).replace(//g,(function(){return a})),RegExp(e,t)}a=o(a).source,e.languages.jsx=e.languages.extend("markup",t),e.languages.jsx.tag.pattern=o(/<\/?(?:[\w.:-]+(?:+(?:[\w.:$-]+(?:=(?:"(?:\\[\s\S]|[^\\"])*"|'(?:\\[\s\S]|[^\\'])*'|[^\s{'"/>=]+|))?|))**\/?)?>/.source),e.languages.jsx.tag.inside.tag.pattern=/^<\/?[^\s>\/]*/,e.languages.jsx.tag.inside["attr-value"].pattern=/=(?!\{)(?:"(?:\\[\s\S]|[^\\"])*"|'(?:\\[\s\S]|[^\\'])*'|[^\s'">]+)/,e.languages.jsx.tag.inside.tag.inside["class-name"]=/^[A-Z]\w*(?:\.[A-Z]\w*)*$/,e.languages.jsx.tag.inside.comment=t.comment,e.languages.insertBefore("inside","attr-name",{spread:{pattern:o(//.source),inside:e.languages.jsx}},e.languages.jsx.tag),e.languages.insertBefore("inside","special-attr",{script:{pattern:o(/=/.source),alias:"language-javascript",inside:{"script-punctuation":{pattern:/^=(?=\{)/,alias:"punctuation"},rest:e.languages.jsx}}},e.languages.jsx.tag);var i=function(e){return e?"string"==typeof e?e:"string"==typeof e.content?e.content:e.content.map(i).join(""):""},l=function(t){for(var n=[],r=0;r0&&n[n.length-1].tagName===i(a.content[0].content[1])&&n.pop():"/>"===a.content[a.content.length-1].content||n.push({tagName:i(a.content[0].content[1]),openedBraces:0}):n.length>0&&"punctuation"===a.type&&"{"===a.content?n[n.length-1].openedBraces++:n.length>0&&n[n.length-1].openedBraces>0&&"punctuation"===a.type&&"}"===a.content?n[n.length-1].openedBraces--:o=!0),(o||"string"==typeof a)&&n.length>0&&0===n[n.length-1].openedBraces){var s=i(a);r0&&("string"==typeof t[r-1]||"plain-text"===t[r-1].type)&&(s=i(t[r-1])+s,t.splice(r-1,1),r--),t[r]=new e.Token("plain-text",s,null,s)}a.content&&"string"!=typeof a.content&&l(a.content)}};e.hooks.add("after-tokenize",(function(e){"jsx"!==e.language&&"tsx"!==e.language||l(e.tokens)}))}(a),function(e){e.languages.diff={coord:[/^(?:\*{3}|-{3}|\+{3}).*$/m,/^@@.*@@$/m,/^\d.*$/m]};var t={"deleted-sign":"-","deleted-arrow":"<","inserted-sign":"+","inserted-arrow":">",unchanged:" ",diff:"!"};Object.keys(t).forEach((function(n){var r=t[n],a=[];/^\w+$/.test(n)||a.push(/\w+/.exec(n)[0]),"diff"===n&&a.push("bold"),e.languages.diff[n]={pattern:RegExp("^(?:["+r+"].*(?:\r\n?|\n|(?![\\s\\S])))+","m"),alias:a,inside:{line:{pattern:/(.)(?=[\s\S]).*(?:\r\n?|\n)?/,lookbehind:!0},prefix:{pattern:/[\s\S]/,alias:/\w+/.exec(n)[0]}}}})),Object.defineProperty(e.languages.diff,"PREFIXES",{value:t})}(a),a.languages.git={comment:/^#.*/m,deleted:/^[-\u2013].*/m,inserted:/^\+.*/m,string:/("|')(?:\\.|(?!\1)[^\\\r\n])*\1/,command:{pattern:/^.*\$ git .*$/m,inside:{parameter:/\s--?\w+/}},coord:/^@@.*@@$/m,"commit-sha1":/^commit \w{40}$/m},a.languages.go=a.languages.extend("clike",{string:{pattern:/(^|[^\\])"(?:\\.|[^"\\\r\n])*"|`[^`]*`/,lookbehind:!0,greedy:!0},keyword:/\b(?:break|case|chan|const|continue|default|defer|else|fallthrough|for|func|go(?:to)?|if|import|interface|map|package|range|return|select|struct|switch|type|var)\b/,boolean:/\b(?:_|false|iota|nil|true)\b/,number:[/\b0(?:b[01_]+|o[0-7_]+)i?\b/i,/\b0x(?:[a-f\d_]+(?:\.[a-f\d_]*)?|\.[a-f\d_]+)(?:p[+-]?\d+(?:_\d+)*)?i?(?!\w)/i,/(?:\b\d[\d_]*(?:\.[\d_]*)?|\B\.\d[\d_]*)(?:e[+-]?[\d_]+)?i?(?!\w)/i],operator:/[*\/%^!=]=?|\+[=+]?|-[=-]?|\|[=|]?|&(?:=|&|\^=?)?|>(?:>=?|=)?|<(?:<=?|=|-)?|:=|\.\.\./,builtin:/\b(?:append|bool|byte|cap|close|complex|complex(?:64|128)|copy|delete|error|float(?:32|64)|u?int(?:8|16|32|64)?|imag|len|make|new|panic|print(?:ln)?|real|recover|rune|string|uintptr)\b/}),a.languages.insertBefore("go","string",{char:{pattern:/'(?:\\.|[^'\\\r\n]){0,10}'/,greedy:!0}}),delete a.languages.go["class-name"],function(e){function t(e,t){return"___"+e.toUpperCase()+t+"___"}Object.defineProperties(e.languages["markup-templating"]={},{buildPlaceholders:{value:function(n,r,a,o){if(n.language===r){var i=n.tokenStack=[];n.code=n.code.replace(a,(function(e){if("function"==typeof o&&!o(e))return e;for(var a,l=i.length;-1!==n.code.indexOf(a=t(r,l));)++l;return i[l]=e,a})),n.grammar=e.languages.markup}}},tokenizePlaceholders:{value:function(n,r){if(n.language===r&&n.tokenStack){n.grammar=e.languages[r];var a=0,o=Object.keys(n.tokenStack);!function i(l){for(var s=0;s=o.length);s++){var u=l[s];if("string"==typeof u||u.content&&"string"==typeof u.content){var c=o[a],p=n.tokenStack[c],d="string"==typeof u?u:u.content,f=t(r,c),m=d.indexOf(f);if(m>-1){++a;var h=d.substring(0,m),g=new e.Token(r,e.tokenize(p,n.grammar),"language-"+r,p),b=d.substring(m+f.length),v=[];h&&v.push.apply(v,i([h])),v.push(g),b&&v.push.apply(v,i([b])),"string"==typeof u?l.splice.apply(l,[s,1].concat(v)):u.content=v}}else u.content&&i(u.content)}return l}(n.tokens)}}}})}(a),function(e){e.languages.handlebars={comment:/\{\{![\s\S]*?\}\}/,delimiter:{pattern:/^\{\{\{?|\}\}\}?$/,alias:"punctuation"},string:/(["'])(?:\\.|(?!\1)[^\\\r\n])*\1/,number:/\b0x[\dA-Fa-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:[Ee][+-]?\d+)?/,boolean:/\b(?:false|true)\b/,block:{pattern:/^(\s*(?:~\s*)?)[#\/]\S+?(?=\s*(?:~\s*)?$|\s)/,lookbehind:!0,alias:"keyword"},brackets:{pattern:/\[[^\]]+\]/,inside:{punctuation:/\[|\]/,variable:/[\s\S]+/}},punctuation:/[!"#%&':()*+,.\/;<=>@\[\\\]^`{|}~]/,variable:/[^!"#%&'()*+,\/;<=>@\[\\\]^`{|}~\s]+/},e.hooks.add("before-tokenize",(function(t){e.languages["markup-templating"].buildPlaceholders(t,"handlebars",/\{\{\{[\s\S]+?\}\}\}|\{\{[\s\S]+?\}\}/g)})),e.hooks.add("after-tokenize",(function(t){e.languages["markup-templating"].tokenizePlaceholders(t,"handlebars")})),e.languages.hbs=e.languages.handlebars}(a),a.languages.json={property:{pattern:/(^|[^\\])"(?:\\.|[^\\"\r\n])*"(?=\s*:)/,lookbehind:!0,greedy:!0},string:{pattern:/(^|[^\\])"(?:\\.|[^\\"\r\n])*"(?!\s*:)/,lookbehind:!0,greedy:!0},comment:{pattern:/\/\/.*|\/\*[\s\S]*?(?:\*\/|$)/,greedy:!0},number:/-?\b\d+(?:\.\d+)?(?:e[+-]?\d+)?\b/i,punctuation:/[{}[\],]/,operator:/:/,boolean:/\b(?:false|true)\b/,null:{pattern:/\bnull\b/,alias:"keyword"}},a.languages.webmanifest=a.languages.json,a.languages.less=a.languages.extend("css",{comment:[/\/\*[\s\S]*?\*\//,{pattern:/(^|[^\\])\/\/.*/,lookbehind:!0}],atrule:{pattern:/@[\w-](?:\((?:[^(){}]|\([^(){}]*\))*\)|[^(){};\s]|\s+(?!\s))*?(?=\s*\{)/,inside:{punctuation:/[:()]/}},selector:{pattern:/(?:@\{[\w-]+\}|[^{};\s@])(?:@\{[\w-]+\}|\((?:[^(){}]|\([^(){}]*\))*\)|[^(){};@\s]|\s+(?!\s))*?(?=\s*\{)/,inside:{variable:/@+[\w-]+/}},property:/(?:@\{[\w-]+\}|[\w-])+(?:\+_?)?(?=\s*:)/,operator:/[+\-*\/]/}),a.languages.insertBefore("less","property",{variable:[{pattern:/@[\w-]+\s*:/,inside:{punctuation:/:/}},/@@?[\w-]+/],"mixin-usage":{pattern:/([{;]\s*)[.#](?!\d)[\w-].*?(?=[(;])/,lookbehind:!0,alias:"function"}}),a.languages.makefile={comment:{pattern:/(^|[^\\])#(?:\\(?:\r\n|[\s\S])|[^\\\r\n])*/,lookbehind:!0},string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"builtin-target":{pattern:/\.[A-Z][^:#=\s]+(?=\s*:(?!=))/,alias:"builtin"},target:{pattern:/^(?:[^:=\s]|[ \t]+(?![\s:]))+(?=\s*:(?!=))/m,alias:"symbol",inside:{variable:/\$+(?:(?!\$)[^(){}:#=\s]+|(?=[({]))/}},variable:/\$+(?:(?!\$)[^(){}:#=\s]+|\([@*%<^+?][DF]\)|(?=[({]))/,keyword:/-include\b|\b(?:define|else|endef|endif|export|ifn?def|ifn?eq|include|override|private|sinclude|undefine|unexport|vpath)\b/,function:{pattern:/(\()(?:abspath|addsuffix|and|basename|call|dir|error|eval|file|filter(?:-out)?|findstring|firstword|flavor|foreach|guile|if|info|join|lastword|load|notdir|or|origin|patsubst|realpath|shell|sort|strip|subst|suffix|value|warning|wildcard|word(?:list|s)?)(?=[ \t])/,lookbehind:!0},operator:/(?:::|[?:+!])?=|[|@]/,punctuation:/[:;(){}]/},a.languages.objectivec=a.languages.extend("c",{string:{pattern:/@?"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"/,greedy:!0},keyword:/\b(?:asm|auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|in|inline|int|long|register|return|self|short|signed|sizeof|static|struct|super|switch|typedef|typeof|union|unsigned|void|volatile|while)\b|(?:@interface|@end|@implementation|@protocol|@class|@public|@protected|@private|@property|@try|@catch|@finally|@throw|@synthesize|@dynamic|@selector)\b/,operator:/-[->]?|\+\+?|!=?|<>?=?|==?|&&?|\|\|?|[~^%?*\/@]/}),delete a.languages.objectivec["class-name"],a.languages.objc=a.languages.objectivec,a.languages.ocaml={comment:{pattern:/\(\*[\s\S]*?\*\)/,greedy:!0},char:{pattern:/'(?:[^\\\r\n']|\\(?:.|[ox]?[0-9a-f]{1,3}))'/i,greedy:!0},string:[{pattern:/"(?:\\(?:[\s\S]|\r\n)|[^\\\r\n"])*"/,greedy:!0},{pattern:/\{([a-z_]*)\|[\s\S]*?\|\1\}/,greedy:!0}],number:[/\b(?:0b[01][01_]*|0o[0-7][0-7_]*)\b/i,/\b0x[a-f0-9][a-f0-9_]*(?:\.[a-f0-9_]*)?(?:p[+-]?\d[\d_]*)?(?!\w)/i,/\b\d[\d_]*(?:\.[\d_]*)?(?:e[+-]?\d[\d_]*)?(?!\w)/i],directive:{pattern:/\B#\w+/,alias:"property"},label:{pattern:/\B~\w+/,alias:"property"},"type-variable":{pattern:/\B'\w+/,alias:"function"},variant:{pattern:/`\w+/,alias:"symbol"},keyword:/\b(?:as|assert|begin|class|constraint|do|done|downto|else|end|exception|external|for|fun|function|functor|if|in|include|inherit|initializer|lazy|let|match|method|module|mutable|new|nonrec|object|of|open|private|rec|sig|struct|then|to|try|type|val|value|virtual|when|where|while|with)\b/,boolean:/\b(?:false|true)\b/,"operator-like-punctuation":{pattern:/\[[<>|]|[>|]\]|\{<|>\}/,alias:"punctuation"},operator:/\.[.~]|:[=>]|[=<>@^|&+\-*\/$%!?~][!$%&*+\-.\/:<=>?@^|~]*|\b(?:and|asr|land|lor|lsl|lsr|lxor|mod|or)\b/,punctuation:/;;|::|[(){}\[\].,:;#]|\b_\b/},a.languages.python={comment:{pattern:/(^|[^\\])#.*/,lookbehind:!0,greedy:!0},"string-interpolation":{pattern:/(?:f|fr|rf)(?:("""|''')[\s\S]*?\1|("|')(?:\\.|(?!\2)[^\\\r\n])*\2)/i,greedy:!0,inside:{interpolation:{pattern:/((?:^|[^{])(?:\{\{)*)\{(?!\{)(?:[^{}]|\{(?!\{)(?:[^{}]|\{(?!\{)(?:[^{}])+\})+\})+\}/,lookbehind:!0,inside:{"format-spec":{pattern:/(:)[^:(){}]+(?=\}$)/,lookbehind:!0},"conversion-option":{pattern:/![sra](?=[:}]$)/,alias:"punctuation"},rest:null}},string:/[\s\S]+/}},"triple-quoted-string":{pattern:/(?:[rub]|br|rb)?("""|''')[\s\S]*?\1/i,greedy:!0,alias:"string"},string:{pattern:/(?:[rub]|br|rb)?("|')(?:\\.|(?!\1)[^\\\r\n])*\1/i,greedy:!0},function:{pattern:/((?:^|\s)def[ \t]+)[a-zA-Z_]\w*(?=\s*\()/g,lookbehind:!0},"class-name":{pattern:/(\bclass\s+)\w+/i,lookbehind:!0},decorator:{pattern:/(^[\t ]*)@\w+(?:\.\w+)*/m,lookbehind:!0,alias:["annotation","punctuation"],inside:{punctuation:/\./}},keyword:/\b(?:_(?=\s*:)|and|as|assert|async|await|break|case|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|match|nonlocal|not|or|pass|print|raise|return|try|while|with|yield)\b/,builtin:/\b(?:__import__|abs|all|any|apply|ascii|basestring|bin|bool|buffer|bytearray|bytes|callable|chr|classmethod|cmp|coerce|compile|complex|delattr|dict|dir|divmod|enumerate|eval|execfile|file|filter|float|format|frozenset|getattr|globals|hasattr|hash|help|hex|id|input|int|intern|isinstance|issubclass|iter|len|list|locals|long|map|max|memoryview|min|next|object|oct|open|ord|pow|property|range|raw_input|reduce|reload|repr|reversed|round|set|setattr|slice|sorted|staticmethod|str|sum|super|tuple|type|unichr|unicode|vars|xrange|zip)\b/,boolean:/\b(?:False|None|True)\b/,number:/\b0(?:b(?:_?[01])+|o(?:_?[0-7])+|x(?:_?[a-f0-9])+)\b|(?:\b\d+(?:_\d+)*(?:\.(?:\d+(?:_\d+)*)?)?|\B\.\d+(?:_\d+)*)(?:e[+-]?\d+(?:_\d+)*)?j?(?!\w)/i,operator:/[-+%=]=?|!=|:=|\*\*?=?|\/\/?=?|<[<=>]?|>[=>]?|[&|^~]/,punctuation:/[{}[\];(),.:]/},a.languages.python["string-interpolation"].inside.interpolation.inside.rest=a.languages.python,a.languages.py=a.languages.python,a.languages.reason=a.languages.extend("clike",{string:{pattern:/"(?:\\(?:\r\n|[\s\S])|[^\\\r\n"])*"/,greedy:!0},"class-name":/\b[A-Z]\w*/,keyword:/\b(?:and|as|assert|begin|class|constraint|do|done|downto|else|end|exception|external|for|fun|function|functor|if|in|include|inherit|initializer|lazy|let|method|module|mutable|new|nonrec|object|of|open|or|private|rec|sig|struct|switch|then|to|try|type|val|virtual|when|while|with)\b/,operator:/\.{3}|:[:=]|\|>|->|=(?:==?|>)?|<=?|>=?|[|^?'#!~`]|[+\-*\/]\.?|\b(?:asr|land|lor|lsl|lsr|lxor|mod)\b/}),a.languages.insertBefore("reason","class-name",{char:{pattern:/'(?:\\x[\da-f]{2}|\\o[0-3][0-7][0-7]|\\\d{3}|\\.|[^'\\\r\n])'/,greedy:!0},constructor:/\b[A-Z]\w*\b(?!\s*\.)/,label:{pattern:/\b[a-z]\w*(?=::)/,alias:"symbol"}}),delete a.languages.reason.function,function(e){e.languages.sass=e.languages.extend("css",{comment:{pattern:/^([ \t]*)\/[\/*].*(?:(?:\r?\n|\r)\1[ \t].+)*/m,lookbehind:!0,greedy:!0}}),e.languages.insertBefore("sass","atrule",{"atrule-line":{pattern:/^(?:[ \t]*)[@+=].+/m,greedy:!0,inside:{atrule:/(?:@[\w-]+|[+=])/}}}),delete e.languages.sass.atrule;var t=/\$[-\w]+|#\{\$[-\w]+\}/,n=[/[+*\/%]|[=!]=|<=?|>=?|\b(?:and|not|or)\b/,{pattern:/(\s)-(?=\s)/,lookbehind:!0}];e.languages.insertBefore("sass","property",{"variable-line":{pattern:/^[ \t]*\$.+/m,greedy:!0,inside:{punctuation:/:/,variable:t,operator:n}},"property-line":{pattern:/^[ \t]*(?:[^:\s]+ *:.*|:[^:\s].*)/m,greedy:!0,inside:{property:[/[^:\s]+(?=\s*:)/,{pattern:/(:)[^:\s]+/,lookbehind:!0}],punctuation:/:/,variable:t,operator:n,important:e.languages.sass.important}}}),delete e.languages.sass.property,delete e.languages.sass.important,e.languages.insertBefore("sass","punctuation",{selector:{pattern:/^([ \t]*)\S(?:,[^,\r\n]+|[^,\r\n]*)(?:,[^,\r\n]+)*(?:,(?:\r?\n|\r)\1[ \t]+\S(?:,[^,\r\n]+|[^,\r\n]*)(?:,[^,\r\n]+)*)*/m,lookbehind:!0,greedy:!0}})}(a),a.languages.scss=a.languages.extend("css",{comment:{pattern:/(^|[^\\])(?:\/\*[\s\S]*?\*\/|\/\/.*)/,lookbehind:!0},atrule:{pattern:/@[\w-](?:\([^()]+\)|[^()\s]|\s+(?!\s))*?(?=\s+[{;])/,inside:{rule:/@[\w-]+/}},url:/(?:[-a-z]+-)?url(?=\()/i,selector:{pattern:/(?=\S)[^@;{}()]?(?:[^@;{}()\s]|\s+(?!\s)|#\{\$[-\w]+\})+(?=\s*\{(?:\}|\s|[^}][^:{}]*[:{][^}]))/,inside:{parent:{pattern:/&/,alias:"important"},placeholder:/%[-\w]+/,variable:/\$[-\w]+|#\{\$[-\w]+\}/}},property:{pattern:/(?:[-\w]|\$[-\w]|#\{\$[-\w]+\})+(?=\s*:)/,inside:{variable:/\$[-\w]+|#\{\$[-\w]+\}/}}}),a.languages.insertBefore("scss","atrule",{keyword:[/@(?:content|debug|each|else(?: if)?|extend|for|forward|function|if|import|include|mixin|return|use|warn|while)\b/i,{pattern:/( )(?:from|through)(?= )/,lookbehind:!0}]}),a.languages.insertBefore("scss","important",{variable:/\$[-\w]+|#\{\$[-\w]+\}/}),a.languages.insertBefore("scss","function",{"module-modifier":{pattern:/\b(?:as|hide|show|with)\b/i,alias:"keyword"},placeholder:{pattern:/%[-\w]+/,alias:"selector"},statement:{pattern:/\B!(?:default|optional)\b/i,alias:"keyword"},boolean:/\b(?:false|true)\b/,null:{pattern:/\bnull\b/,alias:"keyword"},operator:{pattern:/(\s)(?:[-+*\/%]|[=!]=|<=?|>=?|and|not|or)(?=\s)/,lookbehind:!0}}),a.languages.scss.atrule.inside.rest=a.languages.scss,function(e){var t={pattern:/(\b\d+)(?:%|[a-z]+)/,lookbehind:!0},n={pattern:/(^|[^\w.-])-?(?:\d+(?:\.\d+)?|\.\d+)/,lookbehind:!0},r={comment:{pattern:/(^|[^\\])(?:\/\*[\s\S]*?\*\/|\/\/.*)/,lookbehind:!0},url:{pattern:/\burl\((["']?).*?\1\)/i,greedy:!0},string:{pattern:/("|')(?:(?!\1)[^\\\r\n]|\\(?:\r\n|[\s\S]))*\1/,greedy:!0},interpolation:null,func:null,important:/\B!(?:important|optional)\b/i,keyword:{pattern:/(^|\s+)(?:(?:else|for|if|return|unless)(?=\s|$)|@[\w-]+)/,lookbehind:!0},hexcode:/#[\da-f]{3,6}/i,color:[/\b(?:AliceBlue|AntiqueWhite|Aqua|Aquamarine|Azure|Beige|Bisque|Black|BlanchedAlmond|Blue|BlueViolet|Brown|BurlyWood|CadetBlue|Chartreuse|Chocolate|Coral|CornflowerBlue|Cornsilk|Crimson|Cyan|DarkBlue|DarkCyan|DarkGoldenRod|DarkGr[ae]y|DarkGreen|DarkKhaki|DarkMagenta|DarkOliveGreen|DarkOrange|DarkOrchid|DarkRed|DarkSalmon|DarkSeaGreen|DarkSlateBlue|DarkSlateGr[ae]y|DarkTurquoise|DarkViolet|DeepPink|DeepSkyBlue|DimGr[ae]y|DodgerBlue|FireBrick|FloralWhite|ForestGreen|Fuchsia|Gainsboro|GhostWhite|Gold|GoldenRod|Gr[ae]y|Green|GreenYellow|HoneyDew|HotPink|IndianRed|Indigo|Ivory|Khaki|Lavender|LavenderBlush|LawnGreen|LemonChiffon|LightBlue|LightCoral|LightCyan|LightGoldenRodYellow|LightGr[ae]y|LightGreen|LightPink|LightSalmon|LightSeaGreen|LightSkyBlue|LightSlateGr[ae]y|LightSteelBlue|LightYellow|Lime|LimeGreen|Linen|Magenta|Maroon|MediumAquaMarine|MediumBlue|MediumOrchid|MediumPurple|MediumSeaGreen|MediumSlateBlue|MediumSpringGreen|MediumTurquoise|MediumVioletRed|MidnightBlue|MintCream|MistyRose|Moccasin|NavajoWhite|Navy|OldLace|Olive|OliveDrab|Orange|OrangeRed|Orchid|PaleGoldenRod|PaleGreen|PaleTurquoise|PaleVioletRed|PapayaWhip|PeachPuff|Peru|Pink|Plum|PowderBlue|Purple|Red|RosyBrown|RoyalBlue|SaddleBrown|Salmon|SandyBrown|SeaGreen|SeaShell|Sienna|Silver|SkyBlue|SlateBlue|SlateGr[ae]y|Snow|SpringGreen|SteelBlue|Tan|Teal|Thistle|Tomato|Transparent|Turquoise|Violet|Wheat|White|WhiteSmoke|Yellow|YellowGreen)\b/i,{pattern:/\b(?:hsl|rgb)\(\s*\d{1,3}\s*,\s*\d{1,3}%?\s*,\s*\d{1,3}%?\s*\)\B|\b(?:hsl|rgb)a\(\s*\d{1,3}\s*,\s*\d{1,3}%?\s*,\s*\d{1,3}%?\s*,\s*(?:0|0?\.\d+|1)\s*\)\B/i,inside:{unit:t,number:n,function:/[\w-]+(?=\()/,punctuation:/[(),]/}}],entity:/\\[\da-f]{1,8}/i,unit:t,boolean:/\b(?:false|true)\b/,operator:[/~|[+!\/%<>?=]=?|[-:]=|\*[*=]?|\.{2,3}|&&|\|\||\B-\B|\b(?:and|in|is(?: a| defined| not|nt)?|not|or)\b/],number:n,punctuation:/[{}()\[\];:,]/};r.interpolation={pattern:/\{[^\r\n}:]+\}/,alias:"variable",inside:{delimiter:{pattern:/^\{|\}$/,alias:"punctuation"},rest:r}},r.func={pattern:/[\w-]+\([^)]*\).*/,inside:{function:/^[^(]+/,rest:r}},e.languages.stylus={"atrule-declaration":{pattern:/(^[ \t]*)@.+/m,lookbehind:!0,inside:{atrule:/^@[\w-]+/,rest:r}},"variable-declaration":{pattern:/(^[ \t]*)[\w$-]+\s*.?=[ \t]*(?:\{[^{}]*\}|\S.*|$)/m,lookbehind:!0,inside:{variable:/^\S+/,rest:r}},statement:{pattern:/(^[ \t]*)(?:else|for|if|return|unless)[ \t].+/m,lookbehind:!0,inside:{keyword:/^\S+/,rest:r}},"property-declaration":{pattern:/((?:^|\{)([ \t]*))(?:[\w-]|\{[^}\r\n]+\})+(?:\s*:\s*|[ \t]+)(?!\s)[^{\r\n]*(?:;|[^{\r\n,]$(?!(?:\r?\n|\r)(?:\{|\2[ \t])))/m,lookbehind:!0,inside:{property:{pattern:/^[^\s:]+/,inside:{interpolation:r.interpolation}},rest:r}},selector:{pattern:/(^[ \t]*)(?:(?=\S)(?:[^{}\r\n:()]|::?[\w-]+(?:\([^)\r\n]*\)|(?![\w-]))|\{[^}\r\n]+\})+)(?:(?:\r?\n|\r)(?:\1(?:(?=\S)(?:[^{}\r\n:()]|::?[\w-]+(?:\([^)\r\n]*\)|(?![\w-]))|\{[^}\r\n]+\})+)))*(?:,$|\{|(?=(?:\r?\n|\r)(?:\{|\1[ \t])))/m,lookbehind:!0,inside:{interpolation:r.interpolation,comment:r.comment,punctuation:/[{},]/}},func:r.func,string:r.string,comment:{pattern:/(^|[^\\])(?:\/\*[\s\S]*?\*\/|\/\/.*)/,lookbehind:!0,greedy:!0},interpolation:r.interpolation,punctuation:/[{}()\[\];:.]/}}(a),function(e){var t=e.util.clone(e.languages.typescript);e.languages.tsx=e.languages.extend("jsx",t),delete e.languages.tsx.parameter,delete e.languages.tsx["literal-property"];var n=e.languages.tsx.tag;n.pattern=RegExp(/(^|[^\w$]|(?=<\/))/.source+"(?:"+n.pattern.source+")",n.pattern.flags),n.lookbehind=!0}(a),a.languages.wasm={comment:[/\(;[\s\S]*?;\)/,{pattern:/;;.*/,greedy:!0}],string:{pattern:/"(?:\\[\s\S]|[^"\\])*"/,greedy:!0},keyword:[{pattern:/\b(?:align|offset)=/,inside:{operator:/=/}},{pattern:/\b(?:(?:f32|f64|i32|i64)(?:\.(?:abs|add|and|ceil|clz|const|convert_[su]\/i(?:32|64)|copysign|ctz|demote\/f64|div(?:_[su])?|eqz?|extend_[su]\/i32|floor|ge(?:_[su])?|gt(?:_[su])?|le(?:_[su])?|load(?:(?:8|16|32)_[su])?|lt(?:_[su])?|max|min|mul|neg?|nearest|or|popcnt|promote\/f32|reinterpret\/[fi](?:32|64)|rem_[su]|rot[lr]|shl|shr_[su]|sqrt|store(?:8|16|32)?|sub|trunc(?:_[su]\/f(?:32|64))?|wrap\/i64|xor))?|memory\.(?:grow|size))\b/,inside:{punctuation:/\./}},/\b(?:anyfunc|block|br(?:_if|_table)?|call(?:_indirect)?|data|drop|elem|else|end|export|func|get_(?:global|local)|global|if|import|local|loop|memory|module|mut|nop|offset|param|result|return|select|set_(?:global|local)|start|table|tee_local|then|type|unreachable)\b/],variable:/\$[\w!#$%&'*+\-./:<=>?@\\^`|~]+/,number:/[+-]?\b(?:\d(?:_?\d)*(?:\.\d(?:_?\d)*)?(?:[eE][+-]?\d(?:_?\d)*)?|0x[\da-fA-F](?:_?[\da-fA-F])*(?:\.[\da-fA-F](?:_?[\da-fA-D])*)?(?:[pP][+-]?\d(?:_?\d)*)?)\b|\binf\b|\bnan(?::0x[\da-fA-F](?:_?[\da-fA-D])*)?\b/,punctuation:/[()]/};const o=a},9901:e=>{e.exports&&(e.exports={core:{meta:{path:"components/prism-core.js",option:"mandatory"},core:"Core"},themes:{meta:{path:"themes/{id}.css",link:"index.html?theme={id}",exclusive:!0},prism:{title:"Default",option:"default"},"prism-dark":"Dark","prism-funky":"Funky","prism-okaidia":{title:"Okaidia",owner:"ocodia"},"prism-twilight":{title:"Twilight",owner:"remybach"},"prism-coy":{title:"Coy",owner:"tshedor"},"prism-solarizedlight":{title:"Solarized Light",owner:"hectormatos2011 "},"prism-tomorrow":{title:"Tomorrow Night",owner:"Rosey"}},languages:{meta:{path:"components/prism-{id}",noCSS:!0,examplesPath:"examples/prism-{id}",addCheckAll:!0},markup:{title:"Markup",alias:["html","xml","svg","mathml","ssml","atom","rss"],aliasTitles:{html:"HTML",xml:"XML",svg:"SVG",mathml:"MathML",ssml:"SSML",atom:"Atom",rss:"RSS"},option:"default"},css:{title:"CSS",option:"default",modify:"markup"},clike:{title:"C-like",option:"default"},javascript:{title:"JavaScript",require:"clike",modify:"markup",optional:"regex",alias:"js",option:"default"},abap:{title:"ABAP",owner:"dellagustin"},abnf:{title:"ABNF",owner:"RunDevelopment"},actionscript:{title:"ActionScript",require:"javascript",modify:"markup",owner:"Golmote"},ada:{title:"Ada",owner:"Lucretia"},agda:{title:"Agda",owner:"xy-ren"},al:{title:"AL",owner:"RunDevelopment"},antlr4:{title:"ANTLR4",alias:"g4",owner:"RunDevelopment"},apacheconf:{title:"Apache Configuration",owner:"GuiTeK"},apex:{title:"Apex",require:["clike","sql"],owner:"RunDevelopment"},apl:{title:"APL",owner:"ngn"},applescript:{title:"AppleScript",owner:"Golmote"},aql:{title:"AQL",owner:"RunDevelopment"},arduino:{title:"Arduino",require:"cpp",alias:"ino",owner:"dkern"},arff:{title:"ARFF",owner:"Golmote"},armasm:{title:"ARM Assembly",alias:"arm-asm",owner:"RunDevelopment"},arturo:{title:"Arturo",alias:"art",optional:["bash","css","javascript","markup","markdown","sql"],owner:"drkameleon"},asciidoc:{alias:"adoc",title:"AsciiDoc",owner:"Golmote"},aspnet:{title:"ASP.NET (C#)",require:["markup","csharp"],owner:"nauzilus"},asm6502:{title:"6502 Assembly",owner:"kzurawel"},asmatmel:{title:"Atmel AVR Assembly",owner:"cerkit"},autohotkey:{title:"AutoHotkey",owner:"aviaryan"},autoit:{title:"AutoIt",owner:"Golmote"},avisynth:{title:"AviSynth",alias:"avs",owner:"Zinfidel"},"avro-idl":{title:"Avro IDL",alias:"avdl",owner:"RunDevelopment"},awk:{title:"AWK",alias:"gawk",aliasTitles:{gawk:"GAWK"},owner:"RunDevelopment"},bash:{title:"Bash",alias:["sh","shell"],aliasTitles:{sh:"Shell",shell:"Shell"},owner:"zeitgeist87"},basic:{title:"BASIC",owner:"Golmote"},batch:{title:"Batch",owner:"Golmote"},bbcode:{title:"BBcode",alias:"shortcode",aliasTitles:{shortcode:"Shortcode"},owner:"RunDevelopment"},bbj:{title:"BBj",owner:"hyyan"},bicep:{title:"Bicep",owner:"johnnyreilly"},birb:{title:"Birb",require:"clike",owner:"Calamity210"},bison:{title:"Bison",require:"c",owner:"Golmote"},bnf:{title:"BNF",alias:"rbnf",aliasTitles:{rbnf:"RBNF"},owner:"RunDevelopment"},bqn:{title:"BQN",owner:"yewscion"},brainfuck:{title:"Brainfuck",owner:"Golmote"},brightscript:{title:"BrightScript",owner:"RunDevelopment"},bro:{title:"Bro",owner:"wayward710"},bsl:{title:"BSL (1C:Enterprise)",alias:"oscript",aliasTitles:{oscript:"OneScript"},owner:"Diversus23"},c:{title:"C",require:"clike",owner:"zeitgeist87"},csharp:{title:"C#",require:"clike",alias:["cs","dotnet"],owner:"mvalipour"},cpp:{title:"C++",require:"c",owner:"zeitgeist87"},cfscript:{title:"CFScript",require:"clike",alias:"cfc",owner:"mjclemente"},chaiscript:{title:"ChaiScript",require:["clike","cpp"],owner:"RunDevelopment"},cil:{title:"CIL",owner:"sbrl"},cilkc:{title:"Cilk/C",require:"c",alias:"cilk-c",owner:"OpenCilk"},cilkcpp:{title:"Cilk/C++",require:"cpp",alias:["cilk-cpp","cilk"],owner:"OpenCilk"},clojure:{title:"Clojure",owner:"troglotit"},cmake:{title:"CMake",owner:"mjrogozinski"},cobol:{title:"COBOL",owner:"RunDevelopment"},coffeescript:{title:"CoffeeScript",require:"javascript",alias:"coffee",owner:"R-osey"},concurnas:{title:"Concurnas",alias:"conc",owner:"jasontatton"},csp:{title:"Content-Security-Policy",owner:"ScottHelme"},cooklang:{title:"Cooklang",owner:"ahue"},coq:{title:"Coq",owner:"RunDevelopment"},crystal:{title:"Crystal",require:"ruby",owner:"MakeNowJust"},"css-extras":{title:"CSS Extras",require:"css",modify:"css",owner:"milesj"},csv:{title:"CSV",owner:"RunDevelopment"},cue:{title:"CUE",owner:"RunDevelopment"},cypher:{title:"Cypher",owner:"RunDevelopment"},d:{title:"D",require:"clike",owner:"Golmote"},dart:{title:"Dart",require:"clike",owner:"Golmote"},dataweave:{title:"DataWeave",owner:"machaval"},dax:{title:"DAX",owner:"peterbud"},dhall:{title:"Dhall",owner:"RunDevelopment"},diff:{title:"Diff",owner:"uranusjr"},django:{title:"Django/Jinja2",require:"markup-templating",alias:"jinja2",owner:"romanvm"},"dns-zone-file":{title:"DNS zone file",owner:"RunDevelopment",alias:"dns-zone"},docker:{title:"Docker",alias:"dockerfile",owner:"JustinBeckwith"},dot:{title:"DOT (Graphviz)",alias:"gv",optional:"markup",owner:"RunDevelopment"},ebnf:{title:"EBNF",owner:"RunDevelopment"},editorconfig:{title:"EditorConfig",owner:"osipxd"},eiffel:{title:"Eiffel",owner:"Conaclos"},ejs:{title:"EJS",require:["javascript","markup-templating"],owner:"RunDevelopment",alias:"eta",aliasTitles:{eta:"Eta"}},elixir:{title:"Elixir",owner:"Golmote"},elm:{title:"Elm",owner:"zwilias"},etlua:{title:"Embedded Lua templating",require:["lua","markup-templating"],owner:"RunDevelopment"},erb:{title:"ERB",require:["ruby","markup-templating"],owner:"Golmote"},erlang:{title:"Erlang",owner:"Golmote"},"excel-formula":{title:"Excel Formula",alias:["xlsx","xls"],owner:"RunDevelopment"},fsharp:{title:"F#",require:"clike",owner:"simonreynolds7"},factor:{title:"Factor",owner:"catb0t"},false:{title:"False",owner:"edukisto"},"firestore-security-rules":{title:"Firestore security rules",require:"clike",owner:"RunDevelopment"},flow:{title:"Flow",require:"javascript",owner:"Golmote"},fortran:{title:"Fortran",owner:"Golmote"},ftl:{title:"FreeMarker Template Language",require:"markup-templating",owner:"RunDevelopment"},gml:{title:"GameMaker Language",alias:"gamemakerlanguage",require:"clike",owner:"LiarOnce"},gap:{title:"GAP (CAS)",owner:"RunDevelopment"},gcode:{title:"G-code",owner:"RunDevelopment"},gdscript:{title:"GDScript",owner:"RunDevelopment"},gedcom:{title:"GEDCOM",owner:"Golmote"},gettext:{title:"gettext",alias:"po",owner:"RunDevelopment"},gherkin:{title:"Gherkin",owner:"hason"},git:{title:"Git",owner:"lgiraudel"},glsl:{title:"GLSL",require:"c",owner:"Golmote"},gn:{title:"GN",alias:"gni",owner:"RunDevelopment"},"linker-script":{title:"GNU Linker Script",alias:"ld",owner:"RunDevelopment"},go:{title:"Go",require:"clike",owner:"arnehormann"},"go-module":{title:"Go module",alias:"go-mod",owner:"RunDevelopment"},gradle:{title:"Gradle",require:"clike",owner:"zeabdelkhalek-badido18"},graphql:{title:"GraphQL",optional:"markdown",owner:"Golmote"},groovy:{title:"Groovy",require:"clike",owner:"robfletcher"},haml:{title:"Haml",require:"ruby",optional:["css","css-extras","coffeescript","erb","javascript","less","markdown","scss","textile"],owner:"Golmote"},handlebars:{title:"Handlebars",require:"markup-templating",alias:["hbs","mustache"],aliasTitles:{mustache:"Mustache"},owner:"Golmote"},haskell:{title:"Haskell",alias:"hs",owner:"bholst"},haxe:{title:"Haxe",require:"clike",optional:"regex",owner:"Golmote"},hcl:{title:"HCL",owner:"outsideris"},hlsl:{title:"HLSL",require:"c",owner:"RunDevelopment"},hoon:{title:"Hoon",owner:"matildepark"},http:{title:"HTTP",optional:["csp","css","hpkp","hsts","javascript","json","markup","uri"],owner:"danielgtaylor"},hpkp:{title:"HTTP Public-Key-Pins",owner:"ScottHelme"},hsts:{title:"HTTP Strict-Transport-Security",owner:"ScottHelme"},ichigojam:{title:"IchigoJam",owner:"BlueCocoa"},icon:{title:"Icon",owner:"Golmote"},"icu-message-format":{title:"ICU Message Format",owner:"RunDevelopment"},idris:{title:"Idris",alias:"idr",owner:"KeenS",require:"haskell"},ignore:{title:".ignore",owner:"osipxd",alias:["gitignore","hgignore","npmignore"],aliasTitles:{gitignore:".gitignore",hgignore:".hgignore",npmignore:".npmignore"}},inform7:{title:"Inform 7",owner:"Golmote"},ini:{title:"Ini",owner:"aviaryan"},io:{title:"Io",owner:"AlesTsurko"},j:{title:"J",owner:"Golmote"},java:{title:"Java",require:"clike",owner:"sherblot"},javadoc:{title:"JavaDoc",require:["markup","java","javadoclike"],modify:"java",optional:"scala",owner:"RunDevelopment"},javadoclike:{title:"JavaDoc-like",modify:["java","javascript","php"],owner:"RunDevelopment"},javastacktrace:{title:"Java stack trace",owner:"RunDevelopment"},jexl:{title:"Jexl",owner:"czosel"},jolie:{title:"Jolie",require:"clike",owner:"thesave"},jq:{title:"JQ",owner:"RunDevelopment"},jsdoc:{title:"JSDoc",require:["javascript","javadoclike","typescript"],modify:"javascript",optional:["actionscript","coffeescript"],owner:"RunDevelopment"},"js-extras":{title:"JS Extras",require:"javascript",modify:"javascript",optional:["actionscript","coffeescript","flow","n4js","typescript"],owner:"RunDevelopment"},json:{title:"JSON",alias:"webmanifest",aliasTitles:{webmanifest:"Web App Manifest"},owner:"CupOfTea696"},json5:{title:"JSON5",require:"json",owner:"RunDevelopment"},jsonp:{title:"JSONP",require:"json",owner:"RunDevelopment"},jsstacktrace:{title:"JS stack trace",owner:"sbrl"},"js-templates":{title:"JS Templates",require:"javascript",modify:"javascript",optional:["css","css-extras","graphql","markdown","markup","sql"],owner:"RunDevelopment"},julia:{title:"Julia",owner:"cdagnino"},keepalived:{title:"Keepalived Configure",owner:"dev-itsheng"},keyman:{title:"Keyman",owner:"mcdurdin"},kotlin:{title:"Kotlin",alias:["kt","kts"],aliasTitles:{kts:"Kotlin Script"},require:"clike",owner:"Golmote"},kumir:{title:"KuMir (\u041a\u0443\u041c\u0438\u0440)",alias:"kum",owner:"edukisto"},kusto:{title:"Kusto",owner:"RunDevelopment"},latex:{title:"LaTeX",alias:["tex","context"],aliasTitles:{tex:"TeX",context:"ConTeXt"},owner:"japborst"},latte:{title:"Latte",require:["clike","markup-templating","php"],owner:"nette"},less:{title:"Less",require:"css",optional:"css-extras",owner:"Golmote"},lilypond:{title:"LilyPond",require:"scheme",alias:"ly",owner:"RunDevelopment"},liquid:{title:"Liquid",require:"markup-templating",owner:"cinhtau"},lisp:{title:"Lisp",alias:["emacs","elisp","emacs-lisp"],owner:"JuanCaicedo"},livescript:{title:"LiveScript",owner:"Golmote"},llvm:{title:"LLVM IR",owner:"porglezomp"},log:{title:"Log file",optional:"javastacktrace",owner:"RunDevelopment"},lolcode:{title:"LOLCODE",owner:"Golmote"},lua:{title:"Lua",owner:"Golmote"},magma:{title:"Magma (CAS)",owner:"RunDevelopment"},makefile:{title:"Makefile",owner:"Golmote"},markdown:{title:"Markdown",require:"markup",optional:"yaml",alias:"md",owner:"Golmote"},"markup-templating":{title:"Markup templating",require:"markup",owner:"Golmote"},mata:{title:"Mata",owner:"RunDevelopment"},matlab:{title:"MATLAB",owner:"Golmote"},maxscript:{title:"MAXScript",owner:"RunDevelopment"},mel:{title:"MEL",owner:"Golmote"},mermaid:{title:"Mermaid",owner:"RunDevelopment"},metafont:{title:"METAFONT",owner:"LaeriExNihilo"},mizar:{title:"Mizar",owner:"Golmote"},mongodb:{title:"MongoDB",owner:"airs0urce",require:"javascript"},monkey:{title:"Monkey",owner:"Golmote"},moonscript:{title:"MoonScript",alias:"moon",owner:"RunDevelopment"},n1ql:{title:"N1QL",owner:"TMWilds"},n4js:{title:"N4JS",require:"javascript",optional:"jsdoc",alias:"n4jsd",owner:"bsmith-n4"},"nand2tetris-hdl":{title:"Nand To Tetris HDL",owner:"stephanmax"},naniscript:{title:"Naninovel Script",owner:"Elringus",alias:"nani"},nasm:{title:"NASM",owner:"rbmj"},neon:{title:"NEON",owner:"nette"},nevod:{title:"Nevod",owner:"nezaboodka"},nginx:{title:"nginx",owner:"volado"},nim:{title:"Nim",owner:"Golmote"},nix:{title:"Nix",owner:"Golmote"},nsis:{title:"NSIS",owner:"idleberg"},objectivec:{title:"Objective-C",require:"c",alias:"objc",owner:"uranusjr"},ocaml:{title:"OCaml",owner:"Golmote"},odin:{title:"Odin",owner:"edukisto"},opencl:{title:"OpenCL",require:"c",modify:["c","cpp"],owner:"Milania1"},openqasm:{title:"OpenQasm",alias:"qasm",owner:"RunDevelopment"},oz:{title:"Oz",owner:"Golmote"},parigp:{title:"PARI/GP",owner:"Golmote"},parser:{title:"Parser",require:"markup",owner:"Golmote"},pascal:{title:"Pascal",alias:"objectpascal",aliasTitles:{objectpascal:"Object Pascal"},owner:"Golmote"},pascaligo:{title:"Pascaligo",owner:"DefinitelyNotAGoat"},psl:{title:"PATROL Scripting Language",owner:"bertysentry"},pcaxis:{title:"PC-Axis",alias:"px",owner:"RunDevelopment"},peoplecode:{title:"PeopleCode",alias:"pcode",owner:"RunDevelopment"},perl:{title:"Perl",owner:"Golmote"},php:{title:"PHP",require:"markup-templating",owner:"milesj"},phpdoc:{title:"PHPDoc",require:["php","javadoclike"],modify:"php",owner:"RunDevelopment"},"php-extras":{title:"PHP Extras",require:"php",modify:"php",owner:"milesj"},"plant-uml":{title:"PlantUML",alias:"plantuml",owner:"RunDevelopment"},plsql:{title:"PL/SQL",require:"sql",owner:"Golmote"},powerquery:{title:"PowerQuery",alias:["pq","mscript"],owner:"peterbud"},powershell:{title:"PowerShell",owner:"nauzilus"},processing:{title:"Processing",require:"clike",owner:"Golmote"},prolog:{title:"Prolog",owner:"Golmote"},promql:{title:"PromQL",owner:"arendjr"},properties:{title:".properties",owner:"Golmote"},protobuf:{title:"Protocol Buffers",require:"clike",owner:"just-boris"},pug:{title:"Pug",require:["markup","javascript"],optional:["coffeescript","ejs","handlebars","less","livescript","markdown","scss","stylus","twig"],owner:"Golmote"},puppet:{title:"Puppet",owner:"Golmote"},pure:{title:"Pure",optional:["c","cpp","fortran"],owner:"Golmote"},purebasic:{title:"PureBasic",require:"clike",alias:"pbfasm",owner:"HeX0R101"},purescript:{title:"PureScript",require:"haskell",alias:"purs",owner:"sriharshachilakapati"},python:{title:"Python",alias:"py",owner:"multipetros"},qsharp:{title:"Q#",require:"clike",alias:"qs",owner:"fedonman"},q:{title:"Q (kdb+ database)",owner:"Golmote"},qml:{title:"QML",require:"javascript",owner:"RunDevelopment"},qore:{title:"Qore",require:"clike",owner:"temnroegg"},r:{title:"R",owner:"Golmote"},racket:{title:"Racket",require:"scheme",alias:"rkt",owner:"RunDevelopment"},cshtml:{title:"Razor C#",alias:"razor",require:["markup","csharp"],optional:["css","css-extras","javascript","js-extras"],owner:"RunDevelopment"},jsx:{title:"React JSX",require:["markup","javascript"],optional:["jsdoc","js-extras","js-templates"],owner:"vkbansal"},tsx:{title:"React TSX",require:["jsx","typescript"]},reason:{title:"Reason",require:"clike",owner:"Golmote"},regex:{title:"Regex",owner:"RunDevelopment"},rego:{title:"Rego",owner:"JordanSh"},renpy:{title:"Ren'py",alias:"rpy",owner:"HyuchiaDiego"},rescript:{title:"ReScript",alias:"res",owner:"vmarcosp"},rest:{title:"reST (reStructuredText)",owner:"Golmote"},rip:{title:"Rip",owner:"ravinggenius"},roboconf:{title:"Roboconf",owner:"Golmote"},robotframework:{title:"Robot Framework",alias:"robot",owner:"RunDevelopment"},ruby:{title:"Ruby",require:"clike",alias:"rb",owner:"samflores"},rust:{title:"Rust",owner:"Golmote"},sas:{title:"SAS",optional:["groovy","lua","sql"],owner:"Golmote"},sass:{title:"Sass (Sass)",require:"css",optional:"css-extras",owner:"Golmote"},scss:{title:"Sass (SCSS)",require:"css",optional:"css-extras",owner:"MoOx"},scala:{title:"Scala",require:"java",owner:"jozic"},scheme:{title:"Scheme",owner:"bacchus123"},"shell-session":{title:"Shell session",require:"bash",alias:["sh-session","shellsession"],owner:"RunDevelopment"},smali:{title:"Smali",owner:"RunDevelopment"},smalltalk:{title:"Smalltalk",owner:"Golmote"},smarty:{title:"Smarty",require:"markup-templating",optional:"php",owner:"Golmote"},sml:{title:"SML",alias:"smlnj",aliasTitles:{smlnj:"SML/NJ"},owner:"RunDevelopment"},solidity:{title:"Solidity (Ethereum)",alias:"sol",require:"clike",owner:"glachaud"},"solution-file":{title:"Solution file",alias:"sln",owner:"RunDevelopment"},soy:{title:"Soy (Closure Template)",require:"markup-templating",owner:"Golmote"},sparql:{title:"SPARQL",require:"turtle",owner:"Triply-Dev",alias:"rq"},"splunk-spl":{title:"Splunk SPL",owner:"RunDevelopment"},sqf:{title:"SQF: Status Quo Function (Arma 3)",require:"clike",owner:"RunDevelopment"},sql:{title:"SQL",owner:"multipetros"},squirrel:{title:"Squirrel",require:"clike",owner:"RunDevelopment"},stan:{title:"Stan",owner:"RunDevelopment"},stata:{title:"Stata Ado",require:["mata","java","python"],owner:"RunDevelopment"},iecst:{title:"Structured Text (IEC 61131-3)",owner:"serhioromano"},stylus:{title:"Stylus",owner:"vkbansal"},supercollider:{title:"SuperCollider",alias:"sclang",owner:"RunDevelopment"},swift:{title:"Swift",owner:"chrischares"},systemd:{title:"Systemd configuration file",owner:"RunDevelopment"},"t4-templating":{title:"T4 templating",owner:"RunDevelopment"},"t4-cs":{title:"T4 Text Templates (C#)",require:["t4-templating","csharp"],alias:"t4",owner:"RunDevelopment"},"t4-vb":{title:"T4 Text Templates (VB)",require:["t4-templating","vbnet"],owner:"RunDevelopment"},tap:{title:"TAP",owner:"isaacs",require:"yaml"},tcl:{title:"Tcl",owner:"PeterChaplin"},tt2:{title:"Template Toolkit 2",require:["clike","markup-templating"],owner:"gflohr"},textile:{title:"Textile",require:"markup",optional:"css",owner:"Golmote"},toml:{title:"TOML",owner:"RunDevelopment"},tremor:{title:"Tremor",alias:["trickle","troy"],owner:"darach",aliasTitles:{trickle:"trickle",troy:"troy"}},turtle:{title:"Turtle",alias:"trig",aliasTitles:{trig:"TriG"},owner:"jakubklimek"},twig:{title:"Twig",require:"markup-templating",owner:"brandonkelly"},typescript:{title:"TypeScript",require:"javascript",optional:"js-templates",alias:"ts",owner:"vkbansal"},typoscript:{title:"TypoScript",alias:"tsconfig",aliasTitles:{tsconfig:"TSConfig"},owner:"dkern"},unrealscript:{title:"UnrealScript",alias:["uscript","uc"],owner:"RunDevelopment"},uorazor:{title:"UO Razor Script",owner:"jaseowns"},uri:{title:"URI",alias:"url",aliasTitles:{url:"URL"},owner:"RunDevelopment"},v:{title:"V",require:"clike",owner:"taggon"},vala:{title:"Vala",require:"clike",optional:"regex",owner:"TemplarVolk"},vbnet:{title:"VB.Net",require:"basic",owner:"Bigsby"},velocity:{title:"Velocity",require:"markup",owner:"Golmote"},verilog:{title:"Verilog",owner:"a-rey"},vhdl:{title:"VHDL",owner:"a-rey"},vim:{title:"vim",owner:"westonganger"},"visual-basic":{title:"Visual Basic",alias:["vb","vba"],aliasTitles:{vba:"VBA"},owner:"Golmote"},warpscript:{title:"WarpScript",owner:"RunDevelopment"},wasm:{title:"WebAssembly",owner:"Golmote"},"web-idl":{title:"Web IDL",alias:"webidl",owner:"RunDevelopment"},wgsl:{title:"WGSL",owner:"Dr4gonthree"},wiki:{title:"Wiki markup",require:"markup",owner:"Golmote"},wolfram:{title:"Wolfram language",alias:["mathematica","nb","wl"],aliasTitles:{mathematica:"Mathematica",nb:"Mathematica Notebook"},owner:"msollami"},wren:{title:"Wren",owner:"clsource"},xeora:{title:"Xeora",require:"markup",alias:"xeoracube",aliasTitles:{xeoracube:"XeoraCube"},owner:"freakmaxi"},"xml-doc":{title:"XML doc (.net)",require:"markup",modify:["csharp","fsharp","vbnet"],owner:"RunDevelopment"},xojo:{title:"Xojo (REALbasic)",owner:"Golmote"},xquery:{title:"XQuery",require:"markup",owner:"Golmote"},yaml:{title:"YAML",alias:"yml",owner:"hason"},yang:{title:"YANG",owner:"RunDevelopment"},zig:{title:"Zig",owner:"RunDevelopment"}},plugins:{meta:{path:"plugins/{id}/prism-{id}",link:"plugins/{id}/"},"line-highlight":{title:"Line Highlight",description:"Highlights specific lines and/or line ranges."},"line-numbers":{title:"Line Numbers",description:"Line number at the beginning of code lines.",owner:"kuba-kubula"},"show-invisibles":{title:"Show Invisibles",description:"Show hidden characters such as tabs and line breaks.",optional:["autolinker","data-uri-highlight"]},autolinker:{title:"Autolinker",description:"Converts URLs and emails in code to clickable links. Parses Markdown links in comments."},wpd:{title:"WebPlatform Docs",description:'Makes tokens link to WebPlatform.org documentation. The links open in a new tab.'},"custom-class":{title:"Custom Class",description:"This plugin allows you to prefix Prism's default classes (.comment can become .namespace--comment) or replace them with your defined ones (like .editor__comment). You can even add new classes.",owner:"dvkndn",noCSS:!0},"file-highlight":{title:"File Highlight",description:"Fetch external files and highlight them with Prism. Used on the Prism website itself.",noCSS:!0},"show-language":{title:"Show Language",description:"Display the highlighted language in code blocks (inline code does not show the label).",owner:"nauzilus",noCSS:!0,require:"toolbar"},"jsonp-highlight":{title:"JSONP Highlight",description:"Fetch content with JSONP and highlight some interesting content (e.g. GitHub/Gists or Bitbucket API).",noCSS:!0,owner:"nauzilus"},"highlight-keywords":{title:"Highlight Keywords",description:"Adds special CSS classes for each keyword for fine-grained highlighting.",owner:"vkbansal",noCSS:!0},"remove-initial-line-feed":{title:"Remove initial line feed",description:"Removes the initial line feed in code blocks.",owner:"Golmote",noCSS:!0},"inline-color":{title:"Inline color",description:"Adds a small inline preview for colors in style sheets.",require:"css-extras",owner:"RunDevelopment"},previewers:{title:"Previewers",description:"Previewers for angles, colors, gradients, easing and time.",require:"css-extras",owner:"Golmote"},autoloader:{title:"Autoloader",description:"Automatically loads the needed languages to highlight the code blocks.",owner:"Golmote",noCSS:!0},"keep-markup":{title:"Keep Markup",description:"Prevents custom markup from being dropped out during highlighting.",owner:"Golmote",optional:"normalize-whitespace",noCSS:!0},"command-line":{title:"Command Line",description:"Display a command line with a prompt and, optionally, the output/response from the commands.",owner:"chriswells0"},"unescaped-markup":{title:"Unescaped Markup",description:"Write markup without having to escape anything."},"normalize-whitespace":{title:"Normalize Whitespace",description:"Supports multiple operations to normalize whitespace in code blocks.",owner:"zeitgeist87",optional:"unescaped-markup",noCSS:!0},"data-uri-highlight":{title:"Data-URI Highlight",description:"Highlights data-URI contents.",owner:"Golmote",noCSS:!0},toolbar:{title:"Toolbar",description:"Attach a toolbar for plugins to easily register buttons on the top of a code block.",owner:"mAAdhaTTah"},"copy-to-clipboard":{title:"Copy to Clipboard Button",description:"Add a button that copies the code block to the clipboard when clicked.",owner:"mAAdhaTTah",require:"toolbar",noCSS:!0},"download-button":{title:"Download Button",description:"A button in the toolbar of a code block adding a convenient way to download a code file.",owner:"Golmote",require:"toolbar",noCSS:!0},"match-braces":{title:"Match braces",description:"Highlights matching braces.",owner:"RunDevelopment"},"diff-highlight":{title:"Diff Highlight",description:"Highlights the code inside diff blocks.",owner:"RunDevelopment",require:"diff"},"filter-highlight-all":{title:"Filter highlightAll",description:"Filters the elements the highlightAll and highlightAllUnder methods actually highlight.",owner:"RunDevelopment",noCSS:!0},treeview:{title:"Treeview",description:"A language with special styles to highlight file system tree structures.",owner:"Golmote"}}})},2885:(e,t,n)=>{const r=n(9901),a=n(9642),o=new Set;function i(e){void 0===e?e=Object.keys(r.languages).filter((e=>"meta"!=e)):Array.isArray(e)||(e=[e]);const t=[...o,...Object.keys(Prism.languages)];a(r,e,t).load((e=>{if(!(e in r.languages))return void(i.silent||console.warn("Language does not exist: "+e));const t="./prism-"+e;delete n.c[n(6500).resolve(t)],delete Prism.languages[e],n(6500)(t),o.add(e)}))}i.silent=!1,e.exports=i},6726:(e,t,n)=>{var r={"./":2885};function a(e){var t=o(e);return n(t)}function o(e){if(!n.o(r,e)){var t=new Error("Cannot find module '"+e+"'");throw t.code="MODULE_NOT_FOUND",t}return r[e]}a.keys=function(){return Object.keys(r)},a.resolve=o,e.exports=a,a.id=6726},6500:(e,t,n)=>{var r={"./":2885};function a(e){var t=o(e);return n(t)}function o(e){if(!n.o(r,e)){var t=new Error("Cannot find module '"+e+"'");throw t.code="MODULE_NOT_FOUND",t}return r[e]}a.keys=function(){return Object.keys(r)},a.resolve=o,e.exports=a,a.id=6500},9642:e=>{"use strict";var t=function(){var e=function(){};function t(e,t){Array.isArray(e)?e.forEach(t):null!=e&&t(e,0)}function n(e){for(var t={},n=0,r=e.length;n "));var l={},s=e[r];if(s){function u(t){if(!(t in e))throw new Error(r+" depends on an unknown component "+t);if(!(t in l))for(var i in a(t,o),l[t]=!0,n[t])l[i]=!0}t(s.require,u),t(s.optional,u),t(s.modify,u)}n[r]=l,o.pop()}}return function(e){var t=n[e];return t||(a(e,r),t=n[e]),t}}function a(e){for(var t in e)return!0;return!1}return function(o,i,l){var s=function(e){var t={};for(var n in e){var r=e[n];for(var a in r)if("meta"!=a){var o=r[a];t[a]="string"==typeof o?{title:o}:o}}return t}(o),u=function(e){var n;return function(r){if(r in e)return r;if(!n)for(var a in n={},e){var o=e[a];t(o&&o.alias,(function(t){if(t in n)throw new Error(t+" cannot be alias for both "+a+" and "+n[t]);if(t in e)throw new Error(t+" cannot be alias of "+a+" because it is a component.");n[t]=a}))}return n[r]||r}}(s);i=i.map(u),l=(l||[]).map(u);var c=n(i),p=n(l);i.forEach((function e(n){var r=s[n];t(r&&r.require,(function(t){t in p||(c[t]=!0,e(t))}))}));for(var d,f=r(s),m=c;a(m);){for(var h in d={},m){var g=s[h];t(g&&g.modify,(function(e){e in p&&(d[e]=!0)}))}for(var b in p)if(!(b in c))for(var v in f(b))if(v in c){d[b]=!0;break}for(var y in m=d)c[y]=!0}var w={getIds:function(){var e=[];return w.load((function(t){e.push(t)})),e},load:function(t,n){return function(t,n,r,a){var o=a?a.series:void 0,i=a?a.parallel:e,l={},s={};function u(e){if(e in l)return l[e];s[e]=!0;var a,c=[];for(var p in t(e))p in n&&c.push(p);if(0===c.length)a=r(e);else{var d=i(c.map((function(e){var t=u(e);return delete s[e],t})));o?a=o(d,(function(){return r(e)})):r(e)}return l[e]=a}for(var c in n)u(c);var p=[];for(var d in s)p.push(l[d]);return i(p)}(f,c,t,n)}};return w}}();e.exports=t},2703:(e,t,n)=>{"use strict";var r=n(414);function a(){}function o(){}o.resetWarningCache=a,e.exports=function(){function e(e,t,n,a,o,i){if(i!==r){var l=new Error("Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types");throw l.name="Invariant Violation",l}}function t(){return e}e.isRequired=e;var n={array:e,bigint:e,bool:e,func:e,number:e,object:e,string:e,symbol:e,any:e,arrayOf:t,element:e,elementType:e,instanceOf:t,node:e,objectOf:t,oneOf:t,oneOfType:t,shape:t,exact:t,checkPropTypes:o,resetWarningCache:a};return n.PropTypes=n,n}},5697:(e,t,n)=>{e.exports=n(2703)()},414:e=>{"use strict";e.exports="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"},4448:(e,t,n)=>{"use strict";var r=n(7294),a=n(7418),o=n(3840);function i(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n