Reference Source
public class | source

AnnotationsPlugin

Extends:

Plugin → AnnotationsPlugin

AnnotationsPlugin is a Viewer plugin that creates Annotations.

Overview

  • An Annotation is a 3D position with a label attached.
  • Annotations render themselves with HTML elements that float over the canvas; customize the appearance of individual Annotations using HTML template; configure default appearance by setting templates on the AnnotationsPlugin.
  • Dynamically insert data values into each Annotation's HTML templates; configure default values on the AnnotationsPlugin.
  • Optionally configure Annotation with externally-created DOM elements for markers and labels; these override templates and data values.
  • Optionally configure Annotations to hide themselves whenever occluded by Entitys.
  • Optionally configure each Annotation with a position we can jump or fly the Camera to.

Example 1: Loading a model and creating an annotation

In the example below, we'll use a GLTFLoaderPlugin to load a model, and an AnnotationsPlugin to create an Annotation on it.

We'll configure the AnnotationsPlugin with default HTML templates for each Annotation's position (its "marker") and label, along with some default data values to insert into them.

When we create our Annotation, we'll give it some specific data values to insert into the templates, overriding some of the defaults we configured on the plugin. Note the correspondence between the placeholders in the templates and the keys in the values map.

We'll also configure the Annotation to hide itself whenever it's position is occluded by any Entitys (this is default behavior). The Scene periodically occlusion-tests all Annotations on every 20th "tick" (which represents a rendered frame). We can adjust that frequency via property Scene#ticksPerOcclusionTest.

Finally, we'll query the Annotation's position occlusion/visibility status, and subscribe to change events on those properties.

[Run example]

import {Viewer} from "../src/viewer/Viewer.js";
import {GLTFLoaderPlugin} from "../src/plugins/GLTFLoaderPlugin/GLTFLoaderPlugin.js";
import {AnnotationsPlugin} from "../src/plugins/AnnotationsPlugin/AnnotationsPlugin.js";

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

viewer.scene.camera.eye = [-2.37, 18.97, -26.12];
viewer.scene.camera.look = [10.97, 5.82, -11.22];
viewer.scene.camera.up = [0.36, 0.83, 0.40];

const gltfLoader = new GLTFLoaderPlugin(viewer);

const annotations = new AnnotationsPlugin(viewer, {

     // Default HTML template for marker position
     markerHTML: "<div class='annotation-marker' style='background-color: {{markerBGColor}};'>{{glyph}}</div>",

     // Default HTML template for label
     labelHTML: "<div class='annotation-label' style='background-color: {{labelBGColor}};'>" +
     "<div class='annotation-title'>{{title}}</div><div class='annotation-desc'>{{description}}</div></div>",

     // Default values to insert into the marker and label templates
     values: {
         markerBGColor: "red",
         labelBGColor: "red",
         glyph: "X",
         title: "Untitled",
         description: "No description"
     }
});

const model = gltfLoader.load({
     src: "./models/gltf/duplex/scene.gltf"
});

model.on("loaded", () => {

     const entity = viewer.scene.meshes[""];

     // Create an annotation
     const myAnnotation1 = annotations.createAnnotation({

         id: "myAnnotation",

         entity: viewer.scene.objects["2O2Fr$t4X7Zf8NOew3FLOH"], // Optional, associate with an Entity

         worldPos: [0, 0, 0],        // 3D World-space position

         occludable: true,           // Optional, default, makes Annotation invisible when occluded by Entities
         markerShown: true,          // Optional, default is true, makes position visible (when not occluded)
         labelShown: true            // Optional, default is false, makes label visible (when not occluded)

         values: {                   // Optional, overrides AnnotationPlugin's defaults
             glyph: "A",
             title: "My Annotation",
             description: "This is my annotation."
         }
     });

     // Listen for change of the Annotation's 3D World-space position

     myAnnotation1.on("worldPos", function(worldPos) {
         //...
     });

     // Listen for change of the Annotation's 3D View-space position, which happens
     // when either worldPos was updated or the Camera was moved

     myAnnotation1.on("viewPos", function(viewPos) {
         //...
     });

     // Listen for change of the Annotation's 2D Canvas-space position, which happens
     // when worldPos or viewPos was updated, or Camera's projection was updated

     myAnnotation1.on("canvasPos", function(canvasPos) {
         //...
     });

     // Listen for change of Annotation visibility. The Annotation becomes invisible when it falls outside the canvas,
     // or its position is occluded by some Entity. Note that, when not occluded, the position is only
     // shown when Annotation#markerShown is true, and the label is only shown when Annotation#labelShown is true.

     myAnnotation1.on("visible", function(visible) { // Marker visibility has changed
         if (visible) {
             this.log("Annotation is visible");
         } else {
             this.log("Annotation is invisible");
         }
     });

     // Listen for destruction of the Annotation

     myAnnotation1.on("destroyed", () => {
         //...
     });
});

Let's query our Annotation's current position in the World, View and Canvas coordinate systems:

const worldPos  = myAnnotation.worldPos;  // [x,y,z]
const viewPos   = myAnnotation.viewPos;   // [x,y,z]
const canvasPos = myAnnotation.canvasPos; // [x,y]

We can query it's current visibility, which is false when its position is occluded by some Entity:

const visible = myAnnotation1.visible;

To listen for change events on our Annotation's position and visibility:

// World-space position changes when we assign a new value to Annotation#worldPos
myAnnotation1.on("worldPos", (worldPos) => {
    //...
});

// View-space position changes when either worldPos was updated or the Camera was moved
myAnnotation1.on("viewPos", (viewPos) => {
    //...
});

// Canvas-space position changes when worldPos or viewPos was updated, or Camera's projection was updated
myAnnotation1.on("canvasPos", (canvasPos) => {
    //...
});

// Annotation is invisible when its position falls off the canvas or is occluded by some Entity
myAnnotation1.on("visible", (visible) => {
    //...
});

Finally, let's dynamically update the values for a couple of placeholders in our Annotation's label:

myAnnotation1.setValues({
     title: "Here's a new title",
     description: "Here's a new description"
});

Example 2: Creating an Annotation with a unique appearance

Now let's create a second Annotation, this time with its own custom HTML label template, which includes an image. In the Annotation's values, we'll also provide a new title and description, custom colors for the marker and label, plus a URL for the image in the label template. To render its marker, the Annotation will fall back on the AnnotationPlugin's default marker template.

const myAnnotation2 = annotations.createAnnotation({

     id: "myAnnotation2",

     worldPos: [-0.163, 1.810, 7.977],

     occludable: true,
     markerShown: true,
     labelShown: true,

     // Custom label template is the same as the Annotation's, with the addition of an image element
     labelHTML: "<div class='annotation-label' style='background-color: {{labelBGColor}};'>\
         <div class='annotation-title'>{{title}}</div>\
         <div class='annotation-desc'>{{description}}</div>\
         <br><img alt='myImage' width='150px' height='100px' src='{{imageSrc}}'>\
         </div>",

     // Custom template values override all the AnnotationPlugin's defaults, and includes an additional value
     // for the image element's URL
     values: {
         glyph: "A3",
         title: "The West wall",
         description: "Annotations can contain<br>custom HTML like this<br>image:",
         markerBGColor: "green",
         labelBGColor: "green",
         imageSrc: "https://xeokit.io/img/docs/BIMServerLoaderPlugin/schependomlaan.png"
     }
});

Example 3: Creating an Annotation with a camera position

We can optionally configure each Annotation with a position to fly or jump the Camera to.

Let's create another Annotation, this time providing it with eye, look and up properties indicating a viewpoint on whatever it's annotating:

const myAnnotation3 = annotations.createAnnotation({

     id: "myAnnotation3",

     worldPos: [-0.163, 3.810, 7.977],

     eye: [0,0,-10],
     look: [-0.163, 3.810, 7.977],
     up: [0,1,0];

     occludable: true,
     markerShown: true,
     labelShown: true,

     labelHTML: "<div class='annotation-label' style='background-color: {{labelBGColor}};'>\
         <div class='annotation-title'>{{title}}</div>\
         <div class='annotation-desc'>{{description}}</div>\
         <br><img alt='myImage' width='150px' height='100px' src='{{imageSrc}}'>\
         </div>",

     values: {
         glyph: "A3",
         title: "The West wall",
         description: "Annotations can contain<br>custom HTML like this<br>image:",
         markerBGColor: "green",
         labelBGColor: "green",
         imageSrc: "https://xeokit.io/img/docs/BIMServerLoaderPlugin/schependomlaan.png"
     }
});

Now we can fly the Camera to the Annotation's viewpoint, like this:

viewer.cameraFlight.flyTo(myAnnotation3);

Or jump the Camera, like this:

viewer.cameraFlight.jumpTo(myAnnotation3);

Example 4: Creating an Annotation using externally-created DOM elements

Now let's create another Annotation, this time providing it with pre-existing DOM elements for its marker and label. Note that AnnotationsPlugin will ignore any markerHTML, labelHTML or values properties when provide markerElementId or labelElementId.

const myAnnotation2 = annotations.createAnnotation({

     id: "myAnnotation2",

     worldPos: [-0.163, 1.810, 7.977],

     occludable: true,
     markerShown: true,
     labelShown: true,

     markerElementId: "myMarkerElement",
     labelElementId: "myLabelElement"
});

Example 5: Creating annotations by clicking on objects

AnnotationsPlugin makes it easy to create Annotations on the surfaces of Entitys as we click on them.

The AnnotationsPlugin#createAnnotation method can accept a PickResult returned by Scene#pick, from which it initializes the Annotation's Annotation#worldPos and Annotation#entity. Note that this only works when Scene#pick was configured to perform a 3D surface-intersection pick (see Scene#pick for more info).

Let's now extend our example to create an Annotation wherever we click on the surface of of our model:

[Run example]

var i = 1; // Used to create unique Annotation IDs

viewer.scene.input.on("mouseclicked", (coords) => {

    var pickRecord = viewer.scene.pick({
        canvasPos: coords,
        pickSurface: true  // <<------ This causes picking to find the intersection point on the entity
    });

    if (pickRecord) {

        const annotation = annotations.createAnnotation({
             id: "myAnnotationOnClick" + i,
             pickRecord: pickRecord,
             occludable: true,           // Optional, default is true
             markerShown: true,          // Optional, default is true
             labelShown: true,           // Optional, default is true
             values: {                   // HTML template values
                 glyph: "A" + i,
                 title: "My annotation " + i,
                 description: "My description " + i
             },
});

        i++;
     }
});

Constructor Summary

Public Constructor
public

constructor(viewer: Viewer, cfg: Object)

Member Summary

Public Members
public

annotations: {String: Annotation}

The Annotations created by AnnotationsPlugin#createAnnotation, each mapped to its Annotation#id.

Method Summary

Public Methods
public

clear()

Destroys all Annotations.

public

Creates an Annotation.

public

Destroys this AnnotationsPlugin.

public

Destroys an Annotation.

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)

Fires an event at this Plugin.

public

log(msg: String)

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

public

on(event: String, callback: Function)

Subscribes to an event fired at this Plugin.

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: "Annotations"

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

cfg.markerHTML String
  • optional

HTML text template for Annotation markers. Defaults to <div></div>. Ignored on Annotations configured with a markerElementId.

cfg.labelHTML String
  • optional

HTML text template for Annotation labels. Defaults to <div></div>. Ignored on Annotations configured with a labelElementId.

cfg.container HTMLElement
  • optional

Container DOM element for markers and labels. Defaults to document.body.

cfg.values {String: String|Number}
  • optional
  • default: {}

Map of default values to insert into the HTML templates for the marker and label.

Public Members

public annotations: {String: Annotation} source

The Annotations created by AnnotationsPlugin#createAnnotation, each mapped to its Annotation#id.

Public Methods

public clear() source

Destroys all Annotations.

public createAnnotation(params: Object): Annotation source

Creates an Annotation.

The Annotation is then registered by Annotation#id in AnnotationsPlugin#annotations.

Params:

NameTypeAttributeDescription
params Object

Annotation configuration.

params.id String

Unique ID to assign to Annotation#id. The Annotation will be registered by this in AnnotationsPlugin#annotations and Scene.components. Must be unique among all components in the Viewer.

params.markerElementId String
  • optional

ID of pre-existing DOM element to render the marker. This overrides markerHTML and does not support values (data is baked into the label DOM element).

params.labelElementId String
  • optional

ID of pre-existing DOM element to render the label. This overrides labelHTML and does not support values (data is baked into the label DOM element).

params.markerHTML String
  • optional

HTML text template for the Annotation marker. Defaults to the marker HTML given to the AnnotationsPlugin constructor. Ignored if you provide markerElementId.

params.labelHTML String
  • optional

HTML text template for the Annotation label. Defaults to the label HTML given to the AnnotationsPlugin constructor. Ignored if you provide labelElementId.

params.worldPos Number[]
  • optional
  • default: [0,0,0]

World-space position of the Annotation marker, assigned to Annotation#worldPos.

params.entity Entity
  • optional

Optional Entity to associate the Annotation with. Causes Annotation#visible to be false whenever Entity#visible is also false.

params.pickResult PickResult
  • optional

Sets the Annotation's World-space position and direction vector from the given PickResult's PickResult#worldPos and PickResult#worldNormal, and the Annotation's Entity from PickResult#entity. Causes worldPos and entity parameters to be ignored, if they are also given.

params.occludable Boolean
  • optional
  • default: false

Indicates whether or not the Annotation marker and label are hidden whenever the marker occluded by Entitys in the Scene. The Scene periodically occlusion-tests all Annotations on every 20th "tick" (which represents a rendered frame). We can adjust that frequency via property Scene#ticksPerOcclusionTest.

params.values {String: String|Number}
  • optional
  • default: {}

Map of values to insert into the HTML templates for the marker and label. These will be inserted in addition to any values given to the AnnotationsPlugin constructor.

params.markerShown Boolean
  • optional
  • default: true

Whether to initially show the Annotation marker.

params.labelShown Boolean
  • optional
  • default: false

Whether to initially show the Annotation label.

params.eye Number[]
  • optional

Optional World-space position for Camera#eye, used when this Annotation is associated with a Camera position.

params.look Number[]
  • optional

Optional World-space position for Camera#look, used when this Annotation is associated with a Camera position.

params.up Number[]
  • optional

Optional World-space position for Camera#up, used when this Annotation is associated with a Camera position.

Return:

Annotation

The new Annotation.

public destroy() source

Destroys this AnnotationsPlugin.

Destroys all Annotations first.

Override:

Plugin#destroy

public destroyAnnotation(id: String) source

Destroys an Annotation.

Params:

NameTypeAttributeDescription
id String

ID of Annotation to destroy.