##############################
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``:

.. code-block:: js

	// 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:

.. code-block:: js

	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 :doc:`configurable </prog_guide/viewing/selection-and-picking>` with a custom selection operator and setting the appropriate "pick configuration".

.. image:: images/additive-manufacturing-selection.png

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:

.. code-block:: js

    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:

.. code-block:: js

	this.setMatrixText(mainViewer.model.getNodeNetMatrix(nodeId));

.. image:: images/additive-manufacturing-selection-matrix.png
