Setting the selection callback
Summary
In this chapter, we will walk through finishing our Communicator.Webviewer
callbacks, in particular, the selectionArray callback.
Concepts
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
mainViewer.setCallbacks({
sceneReady: () => {
// Additional options for modelStructureReady that we did not want in both viewers
mainViewer.view.getAxisTriad().enable();
mainViewer.view.getNavCube().enable();
mainViewer.view.getNavCube().setAnchor(Communicator.OverlayAnchor.LowerRightCorner);
},
// 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 = selectionEvents.map(sEvent => sEvent.getSelection().getNodeId());
const foundIndex = selectionIds.indexOf(ppNodeId);
if (foundIndex != -1) {
mainViewer.selectionManager.remove(selectionEvents[foundIndex].getSelection());
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)
return;
}
}); // 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:
this.setMatrixText(mainViewer.model.getNodeNetMatrix(nodeId));