
Thatopen Impl Bcf
- 5 installs
- 17 repo stars
- Updated July 8, 2026
- openaec-foundation/thatopen-claude-skill-package
Helps with ai & agent building tasks.
About
thatopen-impl-bcf is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted development.
- thatopen-impl-bcf
- AI & Agent Building
- AI-coding skill
Thatopen Impl Bcf by the numbers
- 5 all-time installs (skills.sh)
- Ranked #13,065 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Jul 26, 2026 (Skillselion catalog sync)
npx skills add https://github.com/openaec-foundation/thatopen-claude-skill-package --skill thatopen-impl-bcfAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 5 |
|---|---|
| repo stars | ★ 17 |
| Last updated | July 8, 2026 |
| Repository | openaec-foundation/thatopen-claude-skill-package ↗ |
What it does
Helps with ai & agent building tasks.
Files
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"` | "" |
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 |
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:
// Silent update — no events fired
topic.title = "Updated Title";
// Reactive update — listeners notified
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:
// Internal (embedded in BCF zip)
{ type: "internal", fileName: "report.pdf", data: Uint8Array, description?: string }
// External (URL reference)
{ type: "external", url: "https://...", description?: string }Viewpoints
Setup
const viewpoints = components.get(OBC.Viewpoints);
viewpoints.world = world; // REQUIRED — default world for viewpoint creationProperties
| Property | Type | Description |
|---|---|---|
list | DataMap<string, Viewpoint> | All viewpoints indexed by GUID |
snapshots | DataMap<string, Uint8Array> | Binary snapshot data |
world | `World \ | null` |
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
// Export all topics
const blob = await bcfTopics.export();
// Export specific topics
const selectedTopics = [...bcfTopics.list.values()].filter(t => t.status === "Active");
const blob = await bcfTopics.export(selectedTopics);
// Download in browser
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = "issues.bcf";
link.click();
URL.revokeObjectURL(url);Import
// From file input
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";
// 1. Get components
const bcfTopics = components.get(OBC.BCFTopics);
const viewpoints = components.get(OBC.Viewpoints);
// 2. Configure BCFTopics (REQUIRED before create/load/export)
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,
});
// 3. Set viewpoints world (REQUIRED before creating viewpoints)
viewpoints.world = world;
// 4. Create topic with viewpoint
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
1. ALWAYS call bcfTopics.setup() before creating, loading, or exporting topics. Without setup, the author field is empty and extensions are not configured. 2. ALWAYS set viewpoints.world before creating viewpoints. Camera capture and snapshot require a valid world reference. 3. ALWAYS set config.version to "2.1" or "3" before exporting. An empty version string produces invalid BCF output. 4. ALWAYS link viewpoints to topics via topic.viewpoints.add(guid), not by storing object references. GUIDs prevent memory leaks. 5. ALWAYS call updateCamera() after creating a viewpoint to capture the current camera state. New viewpoints have no camera data by default. 6. NEVER skip setup() and rely on defaults. All config sets start empty, meaning strict mode would reject every value. 7. NEVER assume imported BCF files specify a version. Use fallbackVersionOnImport to handle version-less files. 8. NEVER mix BCF version strings: use "2.1" or "3" (not "3.0"). 9. 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). 10. NEVER forget to call dispose() or components.dispose() on cleanup. BCFTopics and Viewpoints hold DataMaps that must be freed.
Reference Files
- references/methods.md — BCFTopics, Viewpoints,
Topic, Comment full API reference
- references/examples.md — Create topic, import/
export BCF, viewpoints integration examples
- references/anti-patterns.md — Wrong BCF
version, missing config, memory leaks
Source Verification
All API signatures verified against:
- GitHub:
ThatOpen/engine_componentsmain 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)
BCF & Viewpoints — Anti-Patterns
AP-1: Missing setup() Before Operations
Wrong:
const bcfTopics = components.get(OBC.BCFTopics);
// No setup() call
const topic = bcfTopics.create({ title: "Issue" });
// author is empty string, no extensions configured
const blob = await bcfTopics.export();
// version is empty string — produces invalid BCFCorrect:
const bcfTopics = components.get(OBC.BCFTopics);
bcfTopics.setup({
version: "3",
author: "user@example.com",
types: new Set(["Issue"]),
statuses: new Set(["Active"]),
});
const topic = bcfTopics.create({ title: "Issue" });
const blob = await bcfTopics.export();ALWAYS call setup() with at least version and author before any BCF operations. Without it, exported BCF files lack version info, author attribution, and extension definitions.
---
AP-2: Wrong BCF Version String
Wrong:
bcfTopics.setup({ version: "3.0" }); // "3.0" is not a valid BCFVersion
bcfTopics.setup({ version: "v2.1" }); // "v2.1" is not valid
bcfTopics.setup({ version: 3 }); // number, not stringCorrect:
bcfTopics.setup({ version: "3" }); // BCF 3.0
bcfTopics.setup({ version: "2.1" }); // BCF 2.1The BCFVersion type is "2.1" | "3". NEVER use "3.0", "v2.1", or numeric values.
---
AP-3: Missing Viewpoints World
Wrong:
const viewpoints = components.get(OBC.Viewpoints);
// No world set
const vp = viewpoints.create();
await vp.updateCamera(); // Fails — no world to read camera from
vp.takeSnapshot(); // Fails — no world renderer to captureCorrect:
const viewpoints = components.get(OBC.Viewpoints);
viewpoints.world = world; // Set before creating viewpoints
const vp = viewpoints.create();
await vp.updateCamera();
vp.takeSnapshot();ALWAYS set viewpoints.world before creating viewpoints or calling updateCamera() / takeSnapshot().
---
AP-4: Storing Object References Instead of GUIDs
Wrong:
const topic = bcfTopics.create({ title: "Issue" });
const viewpoint = viewpoints.create();
// Storing object reference — memory leak risk
myApp.savedViewpoint = viewpoint;
myApp.savedTopic = topic;
// Object reference on topic — breaks serialization
topic.viewpoints.add(viewpoint); // Wrong type — expects string GUIDCorrect:
const topic = bcfTopics.create({ title: "Issue" });
const viewpoint = viewpoints.create();
// Store GUIDs
myApp.savedViewpointGuid = viewpoint.guid;
myApp.savedTopicGuid = topic.guid;
// Link via GUID
topic.viewpoints.add(viewpoint.guid);
// Look up later
const vp = viewpoints.list.get(myApp.savedViewpointGuid);
const tp = bcfTopics.list.get(myApp.savedTopicGuid);ALWAYS use GUIDs for cross-references. The DataSet/DataMap collections manage lifecycle; external object references prevent garbage collection.
---
AP-5: Forgetting updateCamera() on New Viewpoints
Wrong:
const vp = viewpoints.create();
vp.title = "Important view";
topic.viewpoints.add(vp.guid);
// Viewpoint has no camera data — go() will not position cameraCorrect:
const vp = viewpoints.create();
vp.title = "Important view";
await vp.updateCamera(true); // Capture camera + snapshot
topic.viewpoints.add(vp.guid);New viewpoints have no camera data by default. ALWAYS call updateCamera() after creation to capture the current camera state.
---
AP-6: Strict Mode with Empty Extensions
Wrong:
bcfTopics.setup({
version: "3",
author: "user@example.com",
strict: true,
// types, statuses, priorities all empty (default)
});
// Throws — "Issue" is not in empty types Set
const topic = bcfTopics.create({ type: "Issue" });Correct:
bcfTopics.setup({
version: "3",
author: "user@example.com",
strict: true,
types: new Set(["Issue", "Request"]),
statuses: new Set(["Active", "Closed"]),
priorities: new Set(["Critical", "Normal"]),
});
const topic = bcfTopics.create({ type: "Issue" }); // WorksWhen strict: true, ALWAYS populate the extension Sets with all allowed values. Empty Sets mean nothing is allowed.
---
AP-7: Importing Without Fallback Version
Wrong:
bcfTopics.setup({ version: "3", author: "user@example.com" });
// fallbackVersionOnImport defaults to null
const data = new Uint8Array(await file.arrayBuffer());
await bcfTopics.load(data);
// If the BCF file has no bcf.version XML, parsing may fail or produce
// unexpected resultsCorrect:
bcfTopics.setup({
version: "3",
author: "user@example.com",
fallbackVersionOnImport: "2.1", // Handle version-less files
ignoreIncompleteTopicsOnImport: true, // Skip malformed topics
});
const data = new Uint8Array(await file.arrayBuffer());
const { topics } = await bcfTopics.load(data);ALWAYS set fallbackVersionOnImport when importing BCF files from external tools. Not all BCF exporters include version metadata.
---
AP-8: Not Disposing BCF Components
Wrong:
// App shutdown — BCFTopics and Viewpoints not disposed
// DataMaps with topics, viewpoints, snapshots remain in memoryCorrect:
// Dispose explicitly
bcfTopics.dispose();
viewpoints.dispose();
// Or via components (preferred)
components.dispose();ALWAYS dispose BCF components on cleanup. The snapshots DataMap in Viewpoints holds binary image data that can consume significant memory.
---
AP-9: Direct Assignment for Reactive UI
Wrong:
// UI table bound to topic events
topic.title = "Updated title"; // No event fired — UI does not update
topic.status = "Resolved"; // No event fired — UI does not updateCorrect:
// Use set() for reactive updates
topic.set({ title: "Updated title", status: "Resolved" });
// Events fire — UI updatesUse set() when UI components listen for topic changes. Direct property assignment is silent. This matters for reactive frameworks and the @thatopen/ui-obc table components.
---
AP-10: Mixing BCF Versions in One Session
Wrong:
// Import BCF 2.1 file
bcfTopics.setup({ version: "2.1", author: "user@example.com" });
await bcfTopics.load(bcf21Data);
// Change version mid-session and export
bcfTopics.setup({ version: "3", author: "user@example.com" });
await bcfTopics.export();
// Topics originally from 2.1 are now exported as 3.0
// Viewpoint XML serialization format changesCorrect:
// Keep consistent version throughout a workflow
bcfTopics.setup({ version: "3", author: "user@example.com" });
// Import handles version detection automatically
await bcfTopics.load(bcf21Data); // Reads version from bcf.version XML
// Export uses the configured version
await bcfTopics.export(); // Exports as v3.0Be intentional about version changes. The load() method reads the BCF file's own version. The export() method uses the configured config.version. Changing version between import and export converts the data, which may lose version-specific features.
---
AP-11: Creating Topics Without Required Fields
Wrong:
const topic = bcfTopics.create();
// title = "BCF Topic" (generic default)
// type = "Issue" (may not be in config types)
// No description, no assigneeCorrect:
const topic = bcfTopics.create({
title: "Specific, descriptive issue title",
type: "Issue",
priority: "Major",
assignedTo: "responsible@company.com",
description: "Clear description of what needs to be addressed",
});ALWAYS provide meaningful title, type, and description when creating topics. Default values produce generic, unhelpful BCF entries.
BCF & Viewpoints — Examples
1. Basic Setup and Topic Creation
import * as OBC from "@thatopen/components";
// Get components
const bcfTopics = components.get(OBC.BCFTopics);
const viewpoints = components.get(OBC.Viewpoints);
// Configure BCFTopics — ALWAYS do this first
bcfTopics.setup({
version: "3",
author: "architect@company.com",
types: new Set(["Issue", "Request", "Comment", "Fault"]),
statuses: new Set(["Active", "InProgress", "Resolved", "Closed"]),
priorities: new Set(["Critical", "Major", "Normal", "Minor"]),
labels: new Set(["Architecture", "Structure", "MEP", "Safety"]),
stages: new Set(["Schematic Design", "Design Development", "Construction"]),
users: new Set(["architect@company.com", "engineer@company.com"]),
});
// Set viewpoints world
viewpoints.world = world;
// Create a topic
const topic = bcfTopics.create({
title: "Missing fire rating on Level 3 wall",
type: "Issue",
priority: "Critical",
status: "Active",
assignedTo: "engineer@company.com",
description: "Wall W-301 between grid B-C lacks required 2-hour fire rating",
stage: "Design Development",
});
// Add labels
topic.labels.add("Architecture");
topic.labels.add("Safety");
// Add a comment
topic.createComment("Detected during model review session 2024-03-15");2. Creating a Viewpoint with Snapshot
// Create viewpoint from current camera state
const viewpoint = viewpoints.create();
viewpoint.title = "View of wall W-301";
// Capture camera position and take snapshot
await viewpoint.updateCamera(true); // true = also take snapshot
// Link viewpoint to topic
topic.viewpoints.add(viewpoint.guid);
// Add selected elements to viewpoint
viewpoint.selectionComponents.add(
"2O2Fr$t4X7Zf8NOew3FLOH", // Wall W-301 IFC GUID
);3. Creating a Viewpoint with Element Selection
const viewpoint = viewpoints.create();
viewpoint.title = "Highlighted clash area";
// Capture camera
await viewpoint.updateCamera();
// Add specific elements by IFC GUID
viewpoint.selectionComponents.add(
"3V$FMCDUfCoPwUaHMPfteW",
"1fIVuvFffDJRV_SJESOtCZ",
);
// Add elements by category using ItemsFinder
const finder = components.get(OBC.ItemsFinder);
const doors = await finder.getItems([{ categories: [/DOOR/] }]);
const fragments = components.get(OBC.FragmentsManager);
const guids = await fragments.modelIdMapToGuids(doors);
viewpoint.selectionComponents.add(...guids);
// Set visibility: hide everything except selected
viewpoint.defaultVisibility = false;
viewpoint.exceptionComponents.add(...guids);4. Applying a Viewpoint
// Navigate camera to viewpoint
const vp = viewpoints.list.get(viewpointGuid);
if (vp) {
await vp.go(); // Sets camera, visibility, colorization, clipping
}5. Export BCF
// Export all topics
const blob = await bcfTopics.export();
downloadBlob(blob, "project-issues.bcf");
// Export only active topics
const activeTopics = [...bcfTopics.list.values()]
.filter(t => t.status === "Active");
const activeBlob = await bcfTopics.export(activeTopics);
downloadBlob(activeBlob, "active-issues.bcf");
// Helper function
function downloadBlob(blob: Blob, filename: string) {
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = filename;
link.click();
URL.revokeObjectURL(url);
}6. Import BCF
// From file input
async function importBCF(file: File) {
const data = new Uint8Array(await file.arrayBuffer());
const { topics, viewpoints: importedVPs } = await bcfTopics.load(data);
console.log(`Imported ${topics.length} topics, ${importedVPs.length} viewpoints`);
return { topics, viewpoints: importedVPs };
}
// With file picker
const input = document.createElement("input");
input.type = "file";
input.accept = ".bcf,.bcfzip";
input.addEventListener("change", async () => {
const file = input.files?.[0];
if (file) await importBCF(file);
});
input.click();7. Listen for BCF Import Events
bcfTopics.onBCFImported.add((importedTopics) => {
console.log(`${importedTopics.length} topics imported`);
for (const topic of importedTopics) {
console.log(`- [${topic.type}] ${topic.title} (${topic.status})`);
}
});8. Auto-link Viewpoints to Topics
// Automatically create a topic for each new viewpoint
viewpoints.list.onItemSet.add(({ value: vp }) => {
const topic = bcfTopics.create({
title: vp.title || "New viewpoint",
type: "Comment",
});
topic.viewpoints.add(vp.guid);
});9. Updating Topic Properties Reactively
// Use set() for reactive updates (fires events for UI listeners)
topic.set({
status: "Resolved",
priority: "Minor",
});
// Add a resolution comment
topic.createComment("Fire rating specification added to wall assembly");10. BCF v2.1 Export
// Configure for BCF 2.1 compatibility
bcfTopics.setup({
version: "2.1",
author: "user@example.com",
types: new Set(["Error", "Warning", "Info"]),
statuses: new Set(["Open", "Closed"]),
priorities: new Set(["High", "Normal", "Low"]),
});
const blob = await bcfTopics.export();11. Strict Mode Validation
bcfTopics.setup({
version: "3",
author: "user@example.com",
types: new Set(["Issue", "Request"]),
statuses: new Set(["Active", "Closed"]),
strict: true, // Enforce validation
});
// This works
const topic = bcfTopics.create({ type: "Issue", status: "Active" });
// This would throw — "Fault" is not in the configured types
// const bad = bcfTopics.create({ type: "Fault" });12. Document References
// Add external document reference
const docGuid = crypto.randomUUID();
bcfTopics.documents.set(docGuid, {
type: "external",
url: "https://docs.example.com/specs/fire-rating.pdf",
description: "Fire rating specification document",
});
topic.documentReferences.add(docGuid);
// Add internal document (embedded in BCF export)
const internalGuid = crypto.randomUUID();
bcfTopics.documents.set(internalGuid, {
type: "internal",
fileName: "screenshot.png",
data: screenshotData, // Uint8Array
description: "Screenshot of the issue area",
});
topic.documentReferences.add(internalGuid);13. Viewpoint with Component Colors
const viewpoint = viewpoints.create();
await viewpoint.updateCamera();
// Color specific components
viewpoint.componentColors.set("#ff0000", [
"2O2Fr$t4X7Zf8NOew3FLOH", // Red for problematic elements
]);
viewpoint.componentColors.set("#00ff00", [
"3V$FMCDUfCoPwUaHMPfteW", // Green for compliant elements
]);
// Apply colorization
viewpoint.setColorizationState(true);14. Viewpoint with Clipping Planes
const viewpoint = viewpoints.create();
await viewpoint.updateCamera();
// Capture current clipping planes
viewpoint.updateClippingPlanes();
// Apply viewpoint (restores clipping state)
await viewpoint.go();15. Complete Workflow: Issue Tracking
import * as OBC from "@thatopen/components";
// Setup
const bcfTopics = components.get(OBC.BCFTopics);
const viewpoints = components.get(OBC.Viewpoints);
bcfTopics.setup({
version: "3",
author: "coordinator@company.com",
types: new Set(["Clash", "Issue", "Request"]),
statuses: new Set(["Open", "InProgress", "Resolved", "Closed"]),
priorities: new Set(["Critical", "Major", "Normal", "Minor"]),
users: new Set(["coordinator@company.com", "architect@company.com"]),
});
viewpoints.world = world;
// 1. Detect issue, create viewpoint
const vp = viewpoints.create();
vp.title = "Clash at grid intersection B-3";
await vp.updateCamera(true);
// 2. Create topic
const topic = bcfTopics.create({
title: "Steel beam clashes with HVAC duct at B-3",
type: "Clash",
priority: "Critical",
assignedTo: "architect@company.com",
description: "Beam HEB300 at +8.400m intersects with DN400 supply duct",
});
topic.viewpoints.add(vp.guid);
topic.createComment("Detected during coordination check. Duct must be rerouted.");
// 3. Export for sharing
const blob = await bcfTopics.export([topic]);
downloadBlob(blob, "clash-B3.bcf");
// 4. Later: resolve
topic.set({ status: "Resolved" });
topic.createComment("Duct rerouted via alternative path above beam");
// 5. Update extensions to reflect current state
bcfTopics.updateExtensions();16. Cleanup
// Dispose individual components
bcfTopics.dispose();
viewpoints.dispose();
// Or let components handle everything
components.dispose();BCF & Viewpoints — Method Reference
BCFTopics
UUID: de977976-e4f6-4e4f-a01a-204727839802 Package: @thatopen/components Implements: Component, Disposable, Configurable<BCFTopicsConfigManager, BCFTopicsConfig>
Constructor
const bcfTopics = components.get(OBC.BCFTopics);ALWAYS use components.get(). NEVER instantiate directly.
setup(config?)
setup(config?: Partial<BCFTopicsConfig>): voidInitializes the component with configuration. Fires onSetup event. ALWAYS call before create(), load(), or export().
create(data?)
create(data?: Partial<BCFTopic>): TopicCreates a new Topic instance. Auto-generates GUID, sets creationDate to now and creationAuthor from config. Optional data initializes properties via topic.set(data).
Returns the created Topic instance, which is also added to list.
load(data)
async load(data: Uint8Array): Promise<{ viewpoints: Viewpoint[]; topics: Topic[] }>Imports a BCF zip file. Parses version from bcf.version XML inside the zip. Processes markup files, viewpoints, and document references. Fires onBCFImported with the imported topics.
Returns both the imported topics and their associated viewpoints.
export(topics?)
async export(topics?: Iterable<Topic>): Promise<Blob>Exports topics to a BCF zip file (Blob). If topics is omitted, exports all topics in list. The zip contains:
bcf.version(XML with version number)bcf.extensions(XML with allowed types/statuses/priorities/etc.)- Per topic:
{guid}/markup.bcf, viewpoint XML files, snapshot images
updateExtensions()
updateExtensions(): voidSynchronizes config sets (types, statuses, priorities, labels, stages, users) with values currently in use across all topics. Call after programmatic topic changes to keep config current.
updateViewpointReferences()
updateViewpointReferences(): voidRemoves stale viewpoint GUIDs from topics when the referenced viewpoints no longer exist in the Viewpoints component list.
dispose()
dispose(): voidClears list and documents, fires onDisposed.
Computed Getters
get usedTypes(): Set<string> // All topic.type values
get usedStatuses(): Set<string> // All topic.status values
get usedPriorities(): Set<string> // All non-null topic.priority values
get usedStages(): Set<string> // All non-null topic.stage values
get usedUsers(): Set<string> // All authors + comment authors
get usedLabels(): Set<string> // All labels across topics---
BCFTopicsConfig
interface BCFTopicsConfig {
version: "2.1" | "3";
author: string;
types: Set<string>;
statuses: Set<string>;
priorities: Set<string>;
labels: Set<string>;
stages: Set<string>;
users: Set<string>;
includeSelectionTag: boolean;
updateExtensionsOnImport: boolean;
strict: boolean;
includeAllExtensionsOnExport: boolean;
fallbackVersionOnImport: BCFVersion | null;
ignoreIncompleteTopicsOnImport: boolean;
exportCustomDataAsLabels: boolean;
}---
Topic
Constructor
Topics are created via bcfTopics.create(), NEVER directly.
set(data)
set(data: Partial<BCFTopic>): TopicBulk-update topic properties. Skips guid to prevent identity changes. Triggers reactive events (unlike direct property assignment).
createComment(text, viewpoint?)
createComment(text: string, viewpoint?: string): CommentCreates a Comment associated with this topic. author and date are auto-set from config. Optional viewpoint is a viewpoint GUID.
toJSON()
toJSON(): BCFApiTopicSerializes to BCF API format (JSON). Converts dates to ISO strings, strips undefined fields.
serialize()
serialize(): stringGenerates BCF markup XML for this topic. Format depends on the BCFTopics config version ("2.1" or "3").
---
BCFTopic Type
interface BCFTopic {
guid: string;
serverAssignedId?: string;
type: string;
status: string;
title: string;
priority?: string;
index?: number;
labels: Set<string>;
creationDate: Date;
creationAuthor: string;
modifiedDate?: Date;
modifiedAuthor?: string;
dueDate?: Date;
assignedTo?: string;
description?: string;
stage?: string;
}---
Comment
Properties
| Property | Type | Mutable | Auto-updated |
|---|---|---|---|
guid | string | No | — |
date | Date | No | — |
author | string | No | — |
comment | string | Yes (setter) | modifiedDate, modifiedAuthor |
viewpoint | string? | Yes | — |
modifiedDate | Date? | — | On comment change |
modifiedAuthor | string? | — | On comment change |
toJSON()
toJSON(): BCFApiCommentSerializes to JSON with ISO date strings.
---
Document References
interface DocumentReference {
type: "internal" | "external";
description?: string;
}
interface InternalDocumentReference extends DocumentReference {
type: "internal";
fileName: string;
data: Uint8Array;
}
interface ExternalDocumentReference extends DocumentReference {
type: "external";
url: string;
}Stored in bcfTopics.documents DataMap, indexed by auto-generated GUID.
---
Viewpoints
UUID: ee867824-a796-408d-8aa0-4e5962a83c66 Package: @thatopen/components Implements: Component, Disposable, Configurable
Constructor
const viewpoints = components.get(OBC.Viewpoints);
viewpoints.world = world; // REQUIREDcreate(data?)
create(data?: Partial<BCFViewpoint>): ViewpointCreates a new Viewpoint instance. Optional data populates from BCF viewpoint data (used during import).
getSnapshotExtension(name)
getSnapshotExtension(name: string): stringExamines snapshot header bytes to detect format. Returns "png" or "jpeg" (default).
dispose()
dispose(): voidClears list and snapshots, fires onDisposed.
---
Viewpoint Instance
updateCamera(takeSnapshot?)
updateCamera(takeSnapshot?: boolean): voidCaptures the current world camera position, direction, projection, and field of view (or scale for ortho). Optionally takes a snapshot.
go(config?)
async go(config?: object): Promise<void>Applies the viewpoint to the world: sets camera position/direction, applies visibility, colorization, and clipping state.
takeSnapshot()
takeSnapshot(): voidCaptures the current world renderer canvas as a Uint8Array and stores it in the Viewpoints manager snapshots map keyed by this viewpoint's GUID.
applyVisibility()
applyVisibility(): voidEnforces defaultVisibility, exception components, and selection components using the Hider component.
setColorizationState(state)
setColorizationState(state: boolean): voidWhen true, applies componentColors highlighting via FragmentsManager. When false, resets colorization.
updateClippingPlanes()
updateClippingPlanes(): voidSyncs the viewpoint's clippingPlanes from the current Clipper state.
toJSON()
toJSON(): BCFViewpointSerializes to BCFViewpoint data object.
serialize(version)
serialize(version: "2.1" | "3"): stringGenerates BCF viewpoint XML. Coordinate transformations differ between v2.1 and v3.0.
---
BCFViewpoint Type
interface BCFViewpoint {
perspective_camera?: ViewpointPerspectiveCamera;
orthogonal_camera?: ViewpointOrthogonalCamera;
components?: ViewpointComponents;
snapshot?: ViewpointSnapshot;
lines?: ViewpointLine[];
clipping_planes?: ViewpointClippingPlane[];
bitmaps?: ViewpointBitmap[];
}Camera Types
interface ViewpointCamera {
camera_view_point: ViewpointVector;
camera_direction: ViewpointVector;
camera_up_vector: ViewpointVector;
aspect_ratio?: number;
}
interface ViewpointPerspectiveCamera extends ViewpointCamera {
field_of_view: number;
}
interface ViewpointOrthogonalCamera extends ViewpointCamera {
view_to_world_scale: number;
}
interface ViewpointVector {
x: number;
y: number;
z: number;
}Component Types
interface ViewpointComponents {
selection?: ViewpointComponent[];
coloring?: ViewpointColoring[];
visibility?: ViewpointVisibility;
}
interface ViewpointComponent {
ifc_guid: string;
authoring_tool_id?: string;
originating_system?: string;
}
interface ViewpointVisibility {
default_visibility: boolean;
exceptions?: ViewpointComponent[];
view_setup_hints?: {
spaces_visible?: boolean;
space_boundaries_visible?: boolean;
openings_visible?: boolean;
};
}
interface ViewpointColoring {
color: string; // hex
components: ViewpointComponent[];
}Supporting Types
interface ViewpointClippingPlane {
location: ViewpointVector;
direction: ViewpointVector;
}
interface ViewpointLine {
start_point: ViewpointVector;
end_point: ViewpointVector;
}
interface ViewpointSnapshot {
snapshot_type: "png" | "jpg";
snapshot_data: string; // base64
}
interface ViewpointBitmap {
bitmap_type: string;
bitmap_data: string; // base64
location: ViewpointVector;
normal: ViewpointVector;
up: ViewpointVector;
height: number;
}