Reference Source
public class | source

BCFViewpointsPlugin

Extends:

Plugin → BCFViewpointsPlugin

Viewer plugin that saves and loads BCF viewpoints as JSON objects.

Overview

BCF is an open standard that enables workflow communications between BIM software tools. An XML schema, called Building Collaboration Format (BCF), encodes messages that inform one BIM tool of issues found by another.

A BCF viewpoint captures a viewpoint of a model that highlights an issue. The viewpoint can then be loaded by another viewer to examine the issue.

Using this plugin, a xeokit Viewer can exchange BCF-encoded viewpoints with other BIM software, allowing us to use the Viewer to report and view issues in BIM models.

This plugin's viewpoints conform to the BCF Version 2.1 specification.

Supported BCF Elements

BCFViewpointsPlugin saves and loads the following state in BCF viewpoints:

Saving a BCF Viewpoint

In the example below we'll create a Viewer, load an .XKT model into it using an XKTLoaderPlugin, slice the model in half using a SectionPlanesPlugin, create a grid ground plane using a LineSet and a 2D plan view using a Bitmap, then use a BCFViewpointsPlugin#getViewpoint to save a viewpoint to JSON, which we'll log to the JavaScript developer console.

import {Viewer, XKTLoaderPlugin, SectionPlanesPlugin,
     LineSet, Bitmap, buildGridGeometry, BCFViewpointsPlugin} from "xeokit-sdk.es.js";

// Create a Viewer
const viewer = new Viewer({
     canvasId: "myCanvas",
     transparent: true
});

// Set camera position and orientation
viewer.scene.camera.eye = [-48.93, 54.54, 50.41];
viewer.scene.camera.look = [0.55, -0.61, -0.55];
viewer.scene.camera.up = [0, -1, 0];
viewer.scene.camera.perspective.fov = 60;

// Add a XKTLoaderPlugin
const xktLoader = new XKTLoaderPlugin(viewer);

// Add a SectionPlanesPlugin
const sectionPlanes = new SectionPlanesPlugin(viewer);

// Add a BCFViewpointsPlugin
const bcfViewpoints = new BCFViewpointsPlugin(viewer);

// Load an .XKT model
const modelNode = xktLoader.load({
     id: "myModel",
     src: "./models/xkt/Schependomlaan.xkt",
     edges: true // Emphasise edges
});

// Slice it in half
sectionPlanes.createSectionPlane({
     id: "myClip",
     pos: [0, 0, 0],
     dir: [0.5, 0.0, 0.5]
});

// Create a bitmap
const bitmap = new Bitmap(viewer.scene, {
    src: "../assets/images/schependomlaanPlanView.png",
     visible: true,
     height: 24.0,
     pos: [-15, 0, -10],
     normal: [0, -1, 0],
     up: [0, 0, 1],
     collidable: false,
     opacity: 1.0,
     clippable: false,
     pickable: true
 });

// Create a grid ground plane
const geometryArrays = buildGridGeometry({
     size: 60,
     divisions: 10
 });

new LineSet(viewer.scene, {
     positions: geometryArrays.positions,
     indices: geometryArrays.indices,
     position: [10,0,10],
     clippable: false
 });

// When model is loaded, select some objects and capture a BCF viewpoint to the console
modelNode.on("loaded", () => {

     const scene = viewer.scene;

     scene.setObjectsSelected([
         "3b2U496P5Ebhz5FROhTwFH",
         "2MGtJUm9nD$Re1_MDIv0g2",
         "3IbuwYOm5EV9Q6cXmwVWqd",
         "3lhisrBxL8xgLCRdxNG$2v",
         "1uDn0xT8LBkP15zQc9MVDW"
     ], true);

     const viewpoint = bcfViewpoints.getViewpoint();
     const viewpointStr = JSON.stringify(viewpoint, null, 4);

     console.log(viewpointStr);
});

The saved BCF viewpoint would look something like below. Note that some elements are truncated for brevity.

{
     "perspective_camera": {
         "camera_view_point": { "x": -48.93, "y": 54.54, "z": 50.41 },
         "camera_direction": { "x": 0.55, "y": -0.61, "z": -0.55},
         "camera_up_vector": { "x": 0.37, "y": -0.41, "z": 0.83 },
         "field_of_view": 60.0
     },
     "lines": [{
         "start_point": { "x": 1.0, "y": 1.0, "z": 1.0 },
         "end_point": { "x": 0.0, "y": 0.0, "z": 0.0 },
         //...(truncated)
     }],
     "bitmaps": [{
         "bitmap_type": "png",
         "bitmap_data": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAB9AAAAdp...", //...(truncated)
         "location": { "x": -15, "y": 10, "z": 0 },
         "normal": { "x": 0, "y": 0, "z": -1 },
         "up": { "x": 0, "y": -1, "z": 0 },
         "height": 24
     }],
     "clipping_planes": [{
         "location": { "x": 0.0, "y": 0.0, "z": 0.0 },
         "direction": { "x": 0.5, "y": 0.0, "z": 0.5 }
     }],
     "snapshot": {
         "snapshot_type": "png",
         "snapshot_data": "data:image/png;base64,......"
     },
     "components": {
         "visibility": {
             "default_visibility": false,
             "exceptions": [{
                     "ifc_guid": "4$cshxZO9AJBebsni$z9Yk",
                     "originating_system": "xeokit.io",
                     "authoring_tool_id": "xeokit/v3.2"
                 },
                 //...
             ]
         },
         "selection": [{
                 "ifc_guid": "4$cshxZO9AJBebsni$z9Yk",
             },
             //...
         ]
     }
}

Saving View Setup Hints

BCFViewpointsPlugin can optionally save hints in the viewpoint, which indicate how to set up the view when loading it again.

Here's the BCFViewpointsPlugin#getViewpoint call again, this time saving some hints:

const viewpoint = bcfViewpoints.getViewpoint({ // Options
    spacesVisible: true, // Force IfcSpace types visible in the viewpoint (default is false)
    spaceBoundariesVisible: false, // Show IfcSpace boundaries in the viewpoint (default is false)
    openingsVisible: true // Force IfcOpening types visible in the viewpoint (default is false)
});

Loading a BCF Viewpoint

Assuming that we have our BCF viewpoint in a JSON object, let's now restore it with BCFViewpointsPlugin#setViewpoint:

bcfViewpoints.setViewpoint(viewpoint);

Handling BCF Incompatibility with xeokit's Camera

xeokit's Camera#look is the current 3D point-of-interest (POI).

A BCF viewpoint, however, has a direction vector instead of a POI, and so BCFViewpointsPlugin#getViewpoint saves xeokit's POI as a normalized vector from Camera#eye to Camera#look, which unfortunately loses that positional information. Loading the viewpoint with BCFViewpointsPlugin#setViewpoint will restore Camera#look to the viewpoint's camera position, offset by the normalized vector.

As shown below, providing a rayCast option to setViewpoint will set Camera#look to the closest surface intersection on the direction vector. Internally, setViewpoint supports this option by firing a ray along the vector, and if that hits an Entity, sets Camera#look to ray's intersection point with the Entity's surface.

bcfViewpoints.setViewpoint(viewpoint, {
     rayCast: true // <<--------------- Attempt to set Camera#look to surface intersection point (default)
});

Dealing With Loaded Models that are not in the Viewpoint

If, for example, we load model "duplex", hide some objects, then save a BCF viewpoint with BCFViewpointsPlugin#getViewpoint, then load another model, "schependomlaan", then load the viewpoint again with BCFViewpointsPlugin#setViewpoint, then sometimes all of the objects in model "schependomlaan" become visible, along with the visible objects in the viewpoint, which belong to model "duplex".

The reason is that, when saving a BCF viewpoint, BCF logic works like the following pseudo code:

If numVisibleObjects < numInvisibleObjects
     save IDs of visible objects in BCF
     exceptions = "visible objects"
else
     save IDS of invisible objects in BCF
     exceptions = "invisible objects"

When loading the viewpoint again:

If exceptions = "visible objects"
     hide all objects
     show visible objects in BCF
else
     show all objects
     hide invisible objects in BCF

When the exception is "visible objects", loading the viewpoint shows all the objects in the first, which includes objects in "schependomlaan", which can be confusing, because those were not even loaded when we first saved the viewpoint..

To solve this, we can supply a defaultInvisible option to BCFViewpointsPlugin#getViewpoint, which will force the plugin to save the IDs of all visible objects while making invisible objects the exception.

That way, when we load the viewpoint again, after loading model "schependomlaan", the plugin will hide all objects in the scene first (which will include objects belonging to model "schependomlaan"), then make the objects in the viewpoint visible (which will only be those of object "duplex").

const viewpoint = bcfViewpoints.getViewpoint({ // Options
    //..
    defaultInvisible: true
});

[Run an example]

Behaviour with XKTLoaderPlugin globalizeObjectIds

Whenever we use XKTLoaderPlugin to load duplicate copies of the same model, after configuring XKTLoaderPlugin#globalizeObjectIds true to avoid Entity ID clashes, this has consequences for BCF viewpoints created by BCFViewpointsPlugin#getViewpoint.

When no duplicate copies of a model are loaded like this, viewpoints created by BCFViewpointsPlugin#getViewpoint will continue to load as usual in other BIM viewers. Conversely, a viewpoint created for a single model in other BIM viewers will continue to load as usual with BCFViewpointsPlugin.

When duplicate copies of a model are loaded, however, viewpoints created by BCFViewpointsPlugin#getViewpoint will contain certain changes that will affect the viewpoint's portability, however. Such viewpoints will use authoring_tool_id fields to save the globalized Entity#id values, which enables the viewpoints to capture the states of the individual Entitys that represent the duplicate IFC elements. Take a look at the following two examples to learn more.

Caveat: when loading a BCF viewpoint, we always assume that we have loaded in our target BIM viewer the same models that were loaded in the viewpoint's original authoring application when the viewpoint was created. In the case of multi-model viewpoints, the target BIM viewer, whether it be xeokit or another BIM viewer, will need to first have those exact models loaded, with their objects having globalized IDs, following the same prefixing scheme we're using in xeokit. Then, the viewpoint's authoring_tool_id fields will be able to resolve to their objects within the target viewer.

Constructor Summary

Public Constructor
public

constructor(viewer: Viewer, cfg: Object)

Member Summary

Public Members
public

Identifies the authoring tool to include in BCF viewpoints saved by this plugin.

public

Identifies the originating system to include in BCF viewpoints saved by this plugin.

Method Summary

Public Methods
public

Destroys this BCFViewpointsPlugin.

public

getViewpoint(options: *): *

Saves viewer state to a BCF viewpoint.

public

setViewpoint(bcfViewpoint: *, options: *)

Sets viewer state to the given BCF viewpoint.

Inherited Summary

From class Plugin
public

ID for this Plugin, unique within its Viewer.

public

The Viewer that contains this Plugin.

public

Destroys this Plugin and removes it from its Viewer.

public

error(msg: String)

Logs an error message to the JavaScript developer console, prefixed with the ID of this Plugin.

public

fire(event: String, value: Object, forget: Boolean)

Fires an event on this Plugin.

public

Returns true if there are any subscribers to the given event on this Plugin.

public

log(msg: String)

Logs a message to the JavaScript developer console, prefixed with the ID of this Plugin.

public

off(subId: String)

Cancels an event subscription that was previously made with Plugin#on or Plugin#once.

public

on(event: String, callback: Function, scope: Object): String

Subscribes to an event on this Plugin.

public

once(event: String, callback: Function, scope: Object)

Subscribes to the next occurrence of the given event, then un-subscribes as soon as the event is subIdd.

public

scheduleTask(task: *)

Schedule a task to perform on the next browser interval

public

warn(msg: String)

Logs a warning message to the JavaScript developer console, prefixed with the ID of this Plugin.

Public Constructors

public constructor(viewer: Viewer, cfg: Object) source

Creates this Plugin and installs it into the given Viewer.

Override:

Plugin#constructor

Params:

NameTypeAttributeDescription
viewer Viewer

The Viewer.

cfg Object

Plugin configuration.

cfg.id String
  • optional
  • default: "BCFViewpoints"

Optional ID for this plugin, so that we can find it within Viewer#plugins.

cfg.originatingSystem String
  • optional

Identifies the originating system for BCF records.

cfg.authoringTool String
  • optional

Identifies the authoring tool for BCF records.

Public Members

public authoringTool: string source

Identifies the authoring tool to include in BCF viewpoints saved by this plugin.

Properties:

NameTypeAttributeDescription
authoringTool *

public originatingSystem: string source

Identifies the originating system to include in BCF viewpoints saved by this plugin.

Properties:

NameTypeAttributeDescription
originatingSystem *

Public Methods

public destroy() source

Destroys this BCFViewpointsPlugin.

Override:

Plugin#destroy

public getViewpoint(options: *): * source

Saves viewer state to a BCF viewpoint.

See BCFViewpointsPlugin class comments for more info.

Params:

NameTypeAttributeDescription
options *
  • optional

Options for getting the viewpoint.

options.spacesVisible Boolean
  • optional
  • default: false

Indicates whether IfcSpace types should be forced visible in the viewpoint.

options.openingsVisible Boolean
  • optional
  • default: false

Indicates whether IfcOpening types should be forced visible in the viewpoint.

options.spaceBoundariesVisible Boolean
  • optional
  • default: false

Indicates whether the boundaries of IfcSpace types should be visible in the viewpoint.

options.spacesTranslucent Boolean
  • optional
  • default: false

Indicates whether IfcSpace types should be forced translucent in the viewpoint.

options.spaceBoundariesTranslucent Boolean
  • optional
  • default: false

Indicates whether the boundaries of IfcSpace types should be forced translucent in the viewpoint.

options.openingsTranslucent Boolean
  • optional
  • default: true

Indicates whether IfcOpening types should be forced translucent in the viewpoint.

options.snapshot Boolean
  • optional
  • default: true

Indicates whether the snapshot should be included in the viewpoint.

options.defaultInvisible Boolean
  • optional
  • default: false

When true, will save the default visibility of all objects as false. This means that when we load the viewpoint again, and there are additional models loaded that were not saved in the viewpoint, those models will be hidden when we load the viewpoint, and that only the objects in the viewpoint will be visible.

options.reverseClippingPlanes Boolean
  • optional
  • default: false

When true, clipping planes are reversed (https://github.com/buildingSMART/BCF-XML/issues/193)

Return:

*

BCF JSON viewpoint object

public setViewpoint(bcfViewpoint: *, options: *) source

Sets viewer state to the given BCF viewpoint.

Note that xeokit's Camera#look is the point-of-interest, whereas the BCF camera_direction is a direction vector. Therefore, when loading a BCF viewpoint, we set Camera#look to the absolute position obtained by offsetting the BCF camera_view_point along camera_direction.

When loading a viewpoint, we also have the option to find Camera#look as the closest point of intersection (on the surface of any visible and pickable Entity) with a 3D ray fired from camera_view_point in the direction of camera_direction.

Params:

NameTypeAttributeDescription
bcfViewpoint *

BCF JSON viewpoint object, shows default visible entities and restores camera to initial default position.

options *
  • optional

Options for setting the viewpoint.

options.rayCast Boolean
  • optional
  • default: true

When true (default), will attempt to set Camera#look to the closest point of surface intersection with a ray fired from the BCF camera_view_point in the direction of camera_direction.

options.immediate Boolean
  • optional
  • default: true

When true (default), immediately set camera position.

options.duration Boolean
  • optional

Flight duration in seconds. Overrides CameraFlightAnimation#duration. Only applies when immediate is false.

options.reset Boolean
  • optional
  • default: true

When true (default), set Entity#xrayed and Entity#highlighted false on all scene objects.

options.reverseClippingPlanes Boolean
  • optional
  • default: false

When true, clipping planes are reversed (https://github.com/buildingSMART/BCF-XML/issues/193)

options.updateCompositeObjects Boolean
  • optional
  • default: false

When true, then when visibility and selection updates refer to composite objects (eg. an IfcBuildingStorey), then this method will apply the updates to objects within those composites.