ThatOpen BCF & Viewpoints
Overview
This skill covers BIM Collaboration Format (BCF) support in
@thatopen/components: the BCFTopics component for issue tracking
and the Viewpoints component for 3D scene state capture. BCF enables
structured communication about BIM model issues between different tools.
Version: @thatopen/components 3.3.x
Prerequisites: thatopen-impl-viewer (world setup), thatopen-core-architecture
Dependencies: jszip (BCF zip), fast-xml-parser (BCF XML)
Component Overview
| Component |
Package |
UUID |
Purpose |
BCFTopics |
@thatopen/components |
de977976-e4f6-4e4f-a01a-204727839802 |
Issue tracking, BCF import/export |
Viewpoints |
@thatopen/components |
ee867824-a796-408d-8aa0-4e5962a83c66 |
3D camera state capture, snapshots |
IDSSpecifications |
@thatopen/components |
— |
IDS validation (separate module) |
BCFTopics
Setup
ALWAYS call setup() with configuration before creating or importing topics:
import * as OBC from "@thatopen/components";
const bcfTopics = components.get(OBC.BCFTopics);
bcfTopics.setup({
version: "3",
author: "user@example.com",
types: new Set(["Issue", "Request", "Comment"]),
statuses: new Set(["Active", "Resolved", "Closed"]),
priorities: new Set(["Critical", "Major", "Normal", "Minor"]),
labels: new Set(["Architecture", "Structure", "MEP"]),
stages: new Set(["Design", "Construction", "Handover"]),
users: new Set(["user@example.com", "reviewer@example.com"]),
});
BCFTopicsConfig
| Property |
Type |
Default |
Description |
version |
"2.1" | "3" |
"" |
BCF version for export |
author |
string |
"" |
User email for topic/comment creation |
types |
Set<string> |
empty |
Allowed topic types |
statuses |
Set<string> |
empty |
Allowed topic statuses |
priorities |
Set<string> |
empty |
Allowed topic priorities |
labels |
Set<string> |
empty |
Allowed topic labels |
stages |
Set<string> |
empty |
Allowed topic stages |
users |
Set<string> |
empty |
Allowed user emails |
strict |
boolean |
false |
Enforce extensions validation |
includeSelectionTag |
boolean |
false |
Include AuthoringSoftwareId in viewpoints |
updateExtensionsOnImport |
boolean |
false |
Auto-update extensions after import |
includeAllExtensionsOnExport |
boolean |
false |
Export all found extensions |
fallbackVersionOnImport |
BCFVersion | null |
null |
Default version if missing in BCF file |
ignoreIncompleteTopicsOnImport |
boolean |
false |
Skip topics missing required fields |
exportCustomDataAsLabels |
boolean |
false |
Export customData as labels |
Strict Mode
When strict: true, all Topic property setters validate against the
configured extensions. Setting topic.type = "Unknown" throws if
"Unknown" is not in config.types. When strict: false (default),
any value is accepted.
Properties
| Property |
Type |
Description |
list |
DataMap<string, Topic> |
All topics indexed by GUID |
documents |
DataMap<string, DocumentReference> |
Internal/external document references |
enabled |
boolean |
Component enabled state |
isSetup |
boolean |
Whether setup() has been called |
Events
| Event |
Payload |
Trigger |
onSetup |
— |
After setup() completes |
onBCFImported |
Topic[] |
After load() imports topics |
onDisposed |
— |
After dispose() |
Methods
| Method |
Returns |
Description |
setup(config?) |
void |
Initialize with configuration |
create(data?) |
Topic |
Create a new topic |
load(data) |
Promise<{viewpoints, topics}> |
Import BCF zip data |
export(topics?) |
Promise<Blob> |
Export topics to BCF zip |
updateExtensions() |
void |
Sync config sets with current topics |
updateViewpointReferences() |
void |
Remove stale viewpoint references |
dispose() |
void |
Clean up all resources |
Computed Getters
| Getter |
Returns |
Description |
usedTypes |
Set<string> |
All types currently in use |
usedStatuses |
Set<string> |
All statuses currently in use |
usedPriorities |
Set<string> |
All priorities currently in use |
usedStages |
Set<string> |
All stages currently in use |
usedUsers |
Set<string> |
All users from authors and comments |
usedLabels |
Set<string> |
All labels across topics |
Topic
Properties
| Property |
Type |
Default |
Description |
guid |
string |
auto-generated |
Unique identifier |
title |
string |
"BCF Topic" |
Topic title |
type |
string |
"Issue" |
Topic type (validated in strict mode) |
status |
string |
"Active" |
Topic status (validated in strict mode) |
priority |
string? |
— |
Priority (validated in strict mode) |
stage |
string? |
— |
Project stage (validated in strict mode) |
assignedTo |
string? |
— |
Assigned user email |
description |
string? |
— |
Topic description |
labels |
Set<string> |
empty |
Topic labels/tags |
dueDate |
Date? |
— |
Due date |
index |
number? |
— |
Display ordering index |
creationDate |
Date |
auto-set |
Creation timestamp |
creationAuthor |
string |
from config |
Author email |
modifiedDate |
Date? |
— |
Last modification timestamp |
modifiedAuthor |
string? |
— |
Last modifier email |
customData |
Record<string, any> |
{} |
Arbitrary metadata |
Topic References (stored as GUIDs)
| Property |
Type |
Description |
viewpoints |
DataSet<string> |
Associated viewpoint GUIDs |
relatedTopics |
DataSet<string> |
Related topic GUIDs (no self-reference) |
comments |
DataMap<string, Comment> |
Comments on this topic |
documentReferences |
DataSet<string> |
Document reference GUIDs |
Topic Methods
| Method |
Returns |
Description |
set(data) |
Topic |
Bulk update properties (skips GUID) |
createComment(text, viewpoint?) |
Comment |
Create a comment on this topic |
toJSON() |
BCFApiTopic |
Serialize to API format |
serialize() |
string |
Generate BCF XML markup |
set() vs Direct Assignment
Direct property assignment updates internally without triggering events.
Use set() to broadcast changes for reactive UI updates:
topic.title = "Updated Title";
topic.set({ title: "Updated Title", status: "Resolved" });
Comment
| Property |
Type |
Description |
guid |
string |
Unique identifier |
date |
Date |
Creation timestamp |
author |
string |
From config at creation time |
comment |
string |
Text (setter updates modifiedDate/modifiedAuthor) |
viewpoint |
string? |
Associated viewpoint GUID |
modifiedDate |
Date? |
Auto-set on comment text change |
modifiedAuthor |
string? |
Auto-set from config on change |
Document References
Two types of document references stored in bcfTopics.documents:
{ type: "internal", fileName: "report.pdf", data: Uint8Array, description?: string }
{ type: "external", url: "https://...", description?: string }
Viewpoints
Setup
const viewpoints = components.get(OBC.Viewpoints);
viewpoints.world = world;
Properties
| Property |
Type |
Description |
list |
DataMap<string, Viewpoint> |
All viewpoints indexed by GUID |
snapshots |
DataMap<string, Uint8Array> |
Binary snapshot data |
world |
World | null |
Default world for creation |
enabled |
boolean |
Defaults to true |
Methods
| Method |
Returns |
Description |
create(data?) |
Viewpoint |
Create a viewpoint (optionally from BCFViewpoint data) |
getSnapshotExtension(name) |
string |
Detect snapshot format from header bytes |
dispose() |
void |
Clean up resources |
Viewpoint Instance
Each Viewpoint captures a complete 3D scene state:
| Property |
Type |
Description |
guid |
string |
Unique identifier |
title |
string? |
Viewpoint name |
camera |
camera data |
Perspective or orthogonal camera settings |
defaultVisibility |
boolean |
Base visibility state |
selectionComponents |
DataSet<string> |
Component GUIDs to highlight |
exceptionComponents |
DataSet<string> |
Visibility override GUIDs |
componentColors |
DataMap<string, string[]> |
Hex color to GUID array |
clippingPlanes |
DataSet<string> |
Enabled clipping plane IDs |
spacesVisible |
boolean |
Show IfcSpace elements |
spaceBoundariesVisible |
boolean |
Show space boundaries |
openingsVisible |
boolean |
Show IfcOpeningElement |
snapshot |
string? |
Snapshot reference ID |
customData |
Record<string, any> |
Arbitrary metadata |
Viewpoint Methods
| Method |
Returns |
Description |
updateCamera(takeSnapshot?) |
void |
Sync from current world camera |
go(config?) |
Promise<void> |
Apply viewpoint to world |
takeSnapshot() |
void |
Capture canvas to snapshots map |
applyVisibility() |
void |
Enforce visibility/exceptions |
setColorizationState(state) |
void |
Apply/reset component colors |
updateClippingPlanes() |
void |
Sync from Clipper component |
toJSON() |
BCFViewpoint |
Serialize to BCF data |
serialize(version) |
string |
Generate BCF XML (v2.1 or v3.0) |
BCF Version Differences
| Feature |
BCF 2.1 |
BCF 3.0 |
| XML schema |
bcf/2.1 namespace |
bcf/3.0 namespace |
| Viewpoint references |
<Viewpoints> element |
<Viewpoints> element |
| Document references |
In markup XML |
In markup XML |
| Labels |
<Labels> element |
<Labels> element |
| Related topics |
<RelatedTopics> |
<RelatedTopics> |
| Extensions file |
bcf.extensions |
bcf.extensions |
Config version value |
"2.1" |
"3" |
ALWAYS set config.version before exporting. The serialization format
of viewpoints and markup XML differs between versions.
BCF Import/Export Workflow
Export
const blob = await bcfTopics.export();
const selectedTopics = [...bcfTopics.list.values()].filter(t => t.status === "Active");
const blob = await bcfTopics.export(selectedTopics);
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = "issues.bcf";
link.click();
URL.revokeObjectURL(url);
Import
const input = document.createElement("input");
input.type = "file";
input.accept = ".bcf,.bcfzip";
input.addEventListener("change", async () => {
const file = input.files?.[0];
if (!file) return;
const data = new Uint8Array(await file.arrayBuffer());
const { topics, viewpoints } = await bcfTopics.load(data);
console.log(`Imported ${topics.length} topics, ${viewpoints.length} viewpoints`);
});
input.click();
Linking Viewpoints to Topics
ALWAYS link viewpoints to topics using GUIDs, not object references:
const viewpoint = viewpoints.create();
viewpoint.title = "Clash at Level 2";
await viewpoint.updateCamera();
viewpoint.takeSnapshot();
const topic = bcfTopics.create({
title: "Steel beam clashes with duct",
type: "Issue",
priority: "Critical",
});
topic.viewpoints.add(viewpoint.guid);
Auto-linking on Creation
viewpoints.list.onItemSet.add(({ value: vp }) => {
const topic = bcfTopics.create();
topic.viewpoints.add(vp.guid);
});
IDSSpecifications Overview
The IDSSpecifications component in @thatopen/components provides
Information Delivery Specification (IDS) validation. IDS is a buildingSMART
standard for specifying information requirements on BIM models.
This component is exported from the openbim module alongside BCFTopics.
For detailed IDS usage, refer to the ThatOpen documentation.
Complete Setup Pattern
import * as OBC from "@thatopen/components";
const bcfTopics = components.get(OBC.BCFTopics);
const viewpoints = components.get(OBC.Viewpoints);
bcfTopics.setup({
version: "3",
author: "user@example.com",
types: new Set(["Issue", "Request", "Comment"]),
statuses: new Set(["Active", "Resolved", "Closed"]),
priorities: new Set(["Critical", "Major", "Normal", "Minor"]),
labels: new Set(["Architecture", "Structure", "MEP"]),
stages: new Set(["Design", "Construction"]),
users: new Set(["user@example.com"]),
strict: false,
});
viewpoints.world = world;
const viewpoint = viewpoints.create();
await viewpoint.updateCamera();
viewpoint.takeSnapshot();
const topic = bcfTopics.create({
title: "Coordination issue at grid A-3",
type: "Issue",
priority: "Major",
assignedTo: "user@example.com",
description: "Steel column interferes with HVAC duct routing",
});
topic.viewpoints.add(viewpoint.guid);
topic.createComment("Please review and propose resolution");
Critical Rules
- ALWAYS call
bcfTopics.setup() before creating, loading, or
exporting topics. Without setup, the author field is empty and
extensions are not configured.
- ALWAYS set
viewpoints.world before creating viewpoints. Camera
capture and snapshot require a valid world reference.
- ALWAYS set
config.version to "2.1" or "3" before exporting.
An empty version string produces invalid BCF output.
- ALWAYS link viewpoints to topics via
topic.viewpoints.add(guid),
not by storing object references. GUIDs prevent memory leaks.
- ALWAYS call
updateCamera() after creating a viewpoint to capture
the current camera state. New viewpoints have no camera data by default.
- NEVER skip
setup() and rely on defaults. All config sets start
empty, meaning strict mode would reject every value.
- NEVER assume imported BCF files specify a version. Use
fallbackVersionOnImport to handle version-less files.
- NEVER mix BCF version strings: use
"2.1" or "3" (not "3.0").
- NEVER store Topic or Viewpoint object references in external data
structures. Use GUIDs from
topic.guid / viewpoint.guid and look
up via bcfTopics.list.get(guid) / viewpoints.list.get(guid).
- NEVER forget to call
dispose() or components.dispose() on
cleanup. BCFTopics and Viewpoints hold DataMaps that must be freed.
Reference Files
Source Verification
All API signatures verified against:
- GitHub:
ThatOpen/engine_components main branch
(packages/core/src/openbim/BCFTopics/, packages/core/src/core/Viewpoints/)
- npm:
@thatopen/components@3.3.3
- Research:
docs/research/vooronderzoek-thatopen.md (Section 7)