BCF visibility
This sample loads the NBU Medical Clinic architecture model, creates BCF topics with viewer.components and viewer.OBC, and opens each topic to apply a different BCF visibility state.
BCF 3.0 visibility is based on DefaultVisibility and Exceptions: when DefaultVisibility is true, exceptions are hidden; when DefaultVisibility is false, exceptions are shown. In the SDK state used below, the same values are written as default_visibility, exceptions, and view_setup_hints. See the BCF 3.0 visibility documentation for the XML property names.
Hide doors and windows
If the list of hidden components is smaller than the list of visible components, keep the model visible by default and put only the hidden components in exceptions. In this sample, all doors and windows are hidden, while the rest of the model stays visible.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { height: 100vh; display: flex; flex-direction: column; }
bim-grid { flex: 1; }
</style>
</head>
<body>
<bim-grid id="ifc-viewer"></bim-grid>
<script type="module">
import { IfcViewer } from "https://cdn.flinker.app/ifc-viewer/v3.2.0/ifc-viewer.es.js";
const viewer = new IfcViewer("#ifc-viewer");
await viewer.ready;
const model = await loadModel(
"NBU_MedicalClinic_Arch-Optimized.ifc",
"https://raw.githubusercontent.com/flinker-app/ifc-sample-files/main/NBU_MedicalClinic/NBU_MedicalClinic_Arch-Optimized.ifc"
);
const hiddenComponents = componentsByType(model, ["IFCDOOR", "IFCWINDOW"]);
const { bytes, topicGuid } = await createBCF("Model without doors and windows", {
visibility: {
default_visibility: true,
exceptions: hiddenComponents
}
});
await viewer.add("model-without-doors-and-windows.bcf", bytes);
await viewer.select(topicGuid);
async function loadModel(name, url) {
const bytes = new Uint8Array(await (await fetch(url)).arrayBuffer());
await viewer.add(name, bytes.slice());
return { name, bytes };
}
function componentsByType(file, types) {
file.text ??= new TextDecoder().decode(file.bytes);
const components = [];
for (const type of types) {
const regex = new RegExp(`#\\d+\\s*=\\s*${type}\\('([^']+)'`, "g");
for (const match of file.text.matchAll(regex)) {
components.push({ ifc_guid: match[1], authoring_tool_id: null });
}
}
return components;
}
async function createBCF(title, state) {
const topics = viewer.components.get(viewer.OBC.BCFTopics);
const viewpoints = viewer.components.get(viewer.OBC.Viewpoints);
setupTopics(topics);
if (!viewpoints.isSetup) viewpoints.setup();
const viewpoint = viewpoints.create({ components: state });
viewpoint.world = viewer.world;
await viewpoint.updateCamera(false);
viewpoint.snapshot = "snapshot";
viewpoints.snapshots.set(viewpoint.snapshot, snapshot());
const topic = topics.create({ title, type: "Coordination", status: "Open" });
topic.viewpoints.add(viewpoint.guid);
try {
const blob = await topics.export([topic]);
return { topicGuid: topic.guid, bytes: new Uint8Array(await blob.arrayBuffer()) };
} finally {
topics.list.delete(topic.guid);
viewpoints.list.delete(viewpoint.guid);
viewpoints.snapshots.delete(viewpoint.snapshot);
}
}
function setupTopics(topics) {
if (!topics.isSetup) {
topics.setup({
version: "3",
author: "docs@example.com",
types: new Set(["Coordination"]),
statuses: new Set(["Open"])
});
}
topics.config.version = "3";
topics.config.author = "docs@example.com";
topics.config.types.add("Coordination");
topics.config.statuses.add("Open");
topics.config.includeSelectionTag = true;
}
function snapshot() {
return Uint8Array.from(
atob("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+/p9sAAAAASUVORK5CYII="),
char => char.charCodeAt(0)
);
}
</script>
</body>
</html>
In BCF XML this corresponds to DefaultVisibility="true". The exception components are the doors and windows to hide.
Show doors and windows only
A common coordination topic isolates a repeated element category for focused review. Hide the model by default and put the doors and windows in exceptions. In this sample, only doors and windows are shown; every other component is hidden.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { height: 100vh; display: flex; flex-direction: column; }
bim-grid { flex: 1; }
</style>
</head>
<body>
<bim-grid id="ifc-viewer"></bim-grid>
<script type="module">
import { IfcViewer } from "https://cdn.flinker.app/ifc-viewer/v3.2.0/ifc-viewer.es.js";
const viewer = new IfcViewer("#ifc-viewer");
await viewer.ready;
const model = await loadModel(
"NBU_MedicalClinic_Arch-Optimized.ifc",
"https://raw.githubusercontent.com/flinker-app/ifc-sample-files/main/NBU_MedicalClinic/NBU_MedicalClinic_Arch-Optimized.ifc"
);
const visibleComponents = componentsByType(model, ["IFCDOOR", "IFCWINDOW"]);
const { bytes, topicGuid } = await createBCF("Doors and windows only", {
visibility: {
default_visibility: false,
exceptions: visibleComponents
}
});
await viewer.add("doors-and-windows-only.bcf", bytes);
await viewer.select(topicGuid);
async function loadModel(name, url) {
const bytes = new Uint8Array(await (await fetch(url)).arrayBuffer());
await viewer.add(name, bytes.slice());
return { name, bytes };
}
function componentsByType(file, types) {
file.text ??= new TextDecoder().decode(file.bytes);
const components = [];
for (const type of types) {
const regex = new RegExp(`#\\d+\\s*=\\s*${type}\\('([^']+)'`, "g");
for (const match of file.text.matchAll(regex)) {
components.push({ ifc_guid: match[1], authoring_tool_id: null });
}
}
return components;
}
async function createBCF(title, state) {
const topics = viewer.components.get(viewer.OBC.BCFTopics);
const viewpoints = viewer.components.get(viewer.OBC.Viewpoints);
setupTopics(topics);
if (!viewpoints.isSetup) viewpoints.setup();
const viewpoint = viewpoints.create({ components: state });
viewpoint.world = viewer.world;
await viewpoint.updateCamera(false);
viewpoint.snapshot = "snapshot";
viewpoints.snapshots.set(viewpoint.snapshot, snapshot());
const topic = topics.create({ title, type: "Coordination", status: "Open" });
topic.viewpoints.add(viewpoint.guid);
try {
const blob = await topics.export([topic]);
return { topicGuid: topic.guid, bytes: new Uint8Array(await blob.arrayBuffer()) };
} finally {
topics.list.delete(topic.guid);
viewpoints.list.delete(viewpoint.guid);
viewpoints.snapshots.delete(viewpoint.snapshot);
}
}
function setupTopics(topics) {
if (!topics.isSetup) {
topics.setup({
version: "3",
author: "docs@example.com",
types: new Set(["Coordination"]),
statuses: new Set(["Open"])
});
}
topics.config.version = "3";
topics.config.author = "docs@example.com";
topics.config.types.add("Coordination");
topics.config.statuses.add("Open");
topics.config.includeSelectionTag = true;
}
function snapshot() {
return Uint8Array.from(
atob("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+/p9sAAAAASUVORK5CYII="),
char => char.charCodeAt(0)
);
}
</script>
</body>
</html>
In BCF XML this corresponds to DefaultVisibility="false". The exception components are the doors and windows to show.
Hide spaces, boundaries, and openings
A typical BCF use case keeps DefaultVisibility=true for normal model elements, but hides spaces, space boundaries, and openings through ViewSetupHints. In this sample, regular model geometry stays visible, while spaces, openings, and space boundaries are hidden.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { height: 100vh; display: flex; flex-direction: column; }
bim-grid { flex: 1; }
</style>
</head>
<body>
<bim-grid id="ifc-viewer"></bim-grid>
<script type="module">
import { IfcViewer } from "https://cdn.flinker.app/ifc-viewer/v3.2.0/ifc-viewer.es.js";
const viewer = new IfcViewer("#ifc-viewer");
await viewer.ready;
const model = await loadModel(
"NBU_MedicalClinic_Arch-Optimized.ifc",
"https://raw.githubusercontent.com/flinker-app/ifc-sample-files/main/NBU_MedicalClinic/NBU_MedicalClinic_Arch-Optimized.ifc"
);
const { bytes, topicGuid } = await createBCF("Building elements only", {
visibility: {
default_visibility: true,
exceptions: [],
view_setup_hints: {
spaces_visible: false,
openings_visible: false,
space_boundaries_visible: false
}
}
});
await viewer.add("building-elements-only.bcf", bytes);
await viewer.select(topicGuid);
async function loadModel(name, url) {
const bytes = new Uint8Array(await (await fetch(url)).arrayBuffer());
await viewer.add(name, bytes.slice());
return { name, bytes };
}
async function createBCF(title, state) {
const topics = viewer.components.get(viewer.OBC.BCFTopics);
const viewpoints = viewer.components.get(viewer.OBC.Viewpoints);
setupTopics(topics);
if (!viewpoints.isSetup) viewpoints.setup();
const viewpoint = viewpoints.create({ components: state });
viewpoint.world = viewer.world;
await viewpoint.updateCamera(false);
viewpoint.snapshot = "snapshot";
viewpoints.snapshots.set(viewpoint.snapshot, snapshot());
const topic = topics.create({ title, type: "Coordination", status: "Open" });
topic.viewpoints.add(viewpoint.guid);
try {
const blob = await topics.export([topic]);
return { topicGuid: topic.guid, bytes: new Uint8Array(await blob.arrayBuffer()) };
} finally {
topics.list.delete(topic.guid);
viewpoints.list.delete(viewpoint.guid);
viewpoints.snapshots.delete(viewpoint.snapshot);
}
}
function setupTopics(topics) {
if (!topics.isSetup) {
topics.setup({
version: "3",
author: "docs@example.com",
types: new Set(["Coordination"]),
statuses: new Set(["Open"])
});
}
topics.config.version = "3";
topics.config.author = "docs@example.com";
topics.config.types.add("Coordination");
topics.config.statuses.add("Open");
topics.config.includeSelectionTag = true;
}
function snapshot() {
return Uint8Array.from(
atob("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+/p9sAAAAASUVORK5CYII="),
char => char.charCodeAt(0)
);
}
</script>
</body>
</html>
The flags are explicit here so the viewpoint intent is clear, even though BCF 3.0 defines these flags as false by default.
Show spaces only
To show spaces only, hide regular model components by default and allow spaces through the visibility state. In this sample, space components are shown, while all non-space components, openings, and space boundaries stay hidden.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { height: 100vh; display: flex; flex-direction: column; }
bim-grid { flex: 1; }
</style>
</head>
<body>
<bim-grid id="ifc-viewer"></bim-grid>
<script type="module">
import { IfcViewer } from "https://cdn.flinker.app/ifc-viewer/v3.2.0/ifc-viewer.es.js";
const viewer = new IfcViewer("#ifc-viewer");
await viewer.ready;
const model = await loadModel(
"NBU_MedicalClinic_Arch-Optimized.ifc",
"https://raw.githubusercontent.com/flinker-app/ifc-sample-files/main/NBU_MedicalClinic/NBU_MedicalClinic_Arch-Optimized.ifc"
);
const spaces = componentsByType(model, ["IFCSPACE"]);
const { bytes, topicGuid } = await createBCF("Room spaces only", {
visibility: {
default_visibility: false,
exceptions: spaces,
view_setup_hints: {
spaces_visible: true,
openings_visible: false,
space_boundaries_visible: false
}
}
});
await viewer.add("room-spaces-only.bcf", bytes);
await viewer.select(topicGuid);
async function loadModel(name, url) {
const bytes = new Uint8Array(await (await fetch(url)).arrayBuffer());
await viewer.add(name, bytes.slice());
return { name, bytes };
}
function componentsByType(file, types) {
file.text ??= new TextDecoder().decode(file.bytes);
const components = [];
for (const type of types) {
const regex = new RegExp(`#\\d+\\s*=\\s*${type}\\('([^']+)'`, "g");
for (const match of file.text.matchAll(regex)) {
components.push({ ifc_guid: match[1], authoring_tool_id: null });
}
}
return components;
}
async function createBCF(title, state) {
const topics = viewer.components.get(viewer.OBC.BCFTopics);
const viewpoints = viewer.components.get(viewer.OBC.Viewpoints);
setupTopics(topics);
if (!viewpoints.isSetup) viewpoints.setup();
const viewpoint = viewpoints.create({ components: state });
viewpoint.world = viewer.world;
await viewpoint.updateCamera(false);
viewpoint.snapshot = "snapshot";
viewpoints.snapshots.set(viewpoint.snapshot, snapshot());
const topic = topics.create({ title, type: "Coordination", status: "Open" });
topic.viewpoints.add(viewpoint.guid);
try {
const blob = await topics.export([topic]);
return { topicGuid: topic.guid, bytes: new Uint8Array(await blob.arrayBuffer()) };
} finally {
topics.list.delete(topic.guid);
viewpoints.list.delete(viewpoint.guid);
viewpoints.snapshots.delete(viewpoint.snapshot);
}
}
function setupTopics(topics) {
if (!topics.isSetup) {
topics.setup({
version: "3",
author: "docs@example.com",
types: new Set(["Coordination"]),
statuses: new Set(["Open"])
});
}
topics.config.version = "3";
topics.config.author = "docs@example.com";
topics.config.types.add("Coordination");
topics.config.statuses.add("Open");
topics.config.includeSelectionTag = true;
}
function snapshot() {
return Uint8Array.from(
atob("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+/p9sAAAAASUVORK5CYII="),
char => char.charCodeAt(0)
);
}
</script>
</body>
</html>
For a full model this means all non-space components remain hidden, while the space components listed in exceptions are visible.