Setting the selection callback


In this chapter, we will walk through finishing our Communicator.Webviewer callbacks, in particular, the selectionArray callback.


  • Using the selectionArray Callback

  • Omitting Selection Results

  • Querying Model and Node Data

By default, HOOPS Communicator enables the js:class:~Communicator.Operator.SelectionOperator and Communicator.OperatorId operators, so you should already be able to interact with the model in this mainViewer. Try clicking on one of the parts on your model to see the selection operator at work. It should highlight, indicating selection - but what else is happening? We can see there are fields for model and node information in our application, but nothing is populated. We will write a Communicator.CallbackMap callback to populate these fields.

Because we are only interacting in our mainViewer object, we only need to add this selectionArray callback on the mainViewer. Let’s look at what information we might want to query from the model to display. We can see fields for the model file name, the file type, node name, node ID, and node transform information. There are additional fields and information you can query of course, and those data items can follow this same pattern.

The selectionArray callback fires whenever Communicator detects a selection event. The callback will query the current state of the Communicator.WebViewer to see which nodes have been selected by the user and currently are being tracked by the Communicator.WebViewer. While the default is to just highlight one node, we can select multiple nodes as well. All currently active and selected nodes are passed to the selectionArray callback each time the callback is invoked.

Given this information, we can use the Communicator.Event.NodeSelectionEvent array returned by the selectionArray callback to gather information or operate on that particular selection. First we will ensure the PrintingPlane object was not selected by the user. Since this was a mesh created a run time rather than a model we wish to query and manipulate, we want it removed if it was selected. We can do this by comparing the nodeId of the printing plane (which we obtained at the time of creation of the PrintingPlane and stored as a property of the class) to the nodeIds of the Communicator.Event.NodeSelectionEvent array items. If the ID is found, we will remove it from the selection array. If it was the only item that fired the callback event, exit the callback function.

Add the following callback for selectionArray to just our mainViewer:

// Set additional callbacks for main viewer only

  sceneReady: () => {
        // Additional options for modelStructureReady that we did not want in both viewers

  // Adding functionality for a selection callback in the mainViewer
  selectionArray: (selectionEvents) => {
        // Do Not Want the Build Plate as a Part of any Model Selection Events
        const ppNodeId = this._printSurfaces[0].getNodeId(); // Node Id of the build plate

        // Return the selection IDs for the current selections, check if the printing plane
        // was selected in the results - if so, remove it
        const selectionIds = => sEvent.getSelection().getNodeId());
        const foundIndex = selectionIds.indexOf(ppNodeId);

        if (foundIndex != -1) {
          selectionEvents.splice(foundIndex, 1);

        // If the printing plane was the only result, then no other selections fired
        // this callback, so exit
        if (selectionEvents.length == 0)

}); // End Callbacks on main viewer

If there were other selections that occurred, we will take the first result of the selection array and display its information. We already have obtained the selection nodeId, so we can now just use the appropriate getter method on the model class to obtain further information. Once this information is obtained, we can update the DOM placeholder elements for this info.

Add the following to the selectionArray callback above:

const nodeId = selectionEvents[0].getSelection().getNodeId();
const modelFileName = mainViewer.model.getModelFileNameFromNode(nodeId);
const modelFileFormat = mainViewer.model.getModelFileTypeFromNode(nodeId);
document.getElementById("model-file-name").innerHTML = modelFileName || "N/A";
document.getElementById("model-file-type").innerHTML = Communicator.FileType[modelFileFormat] || "N/A";
document.getElementById("node-id").innerHTML = nodeId.toString() || "Unknown";
document.getElementById("node-name").innerHTML = mainViewer.model.getNodeName(nodeId) || "Node Name Not Defined";

Rerunning your application, you should now see the “Inspector” component update values depending on the node selection from the user. You may click on the same node again to highlight its parent node and see information higher in the model tree. By default, the selection operator will highlight the sub-entity of the node (i.e. the edge, face, etc), but clicking again will display the entire node information (as it then selects its parent node). This is configurable with a custom selection operator and setting the appropriate “pick configuration”.


Notice the “Node Net Matrix” has not updated. We will create a helper function for this task because we will need to update the matrix again in the next step. Add the following to the setMatrixText function provided with the main class:

setMatrixText(matrix) {
    const ids =
       ['m11', 'm21', 'm31', 'm41',
        'm12', 'm22', 'm32', 'm42',
        'm13', 'm23', 'm33', 'm43',
        'm14', 'm24', 'm34', 'm44'];
    for (let [index, id] of ids.entries()) {
        document.getElementById(id).innerHTML = matrix.m[index].toFixed(1);

And add one last line to the selectionArray callback: