Tutorial contents
In this tutorial, we show how to make objects of the scene hoverable and selectable, and how to link them with the user interface using events of the scene interface.
We will focus on a barebone, fully functional connection, without doing any styling or custom layout: this part is up to the designers and web developers building applications around ShapeDiver.
Demo Model
For this tutorial, we will be working with this example model. It contains several spheres, each of them dependent on two parameters (color and radius). This model can be freely embedded anywhere for testing. Simply use the ticket from the embedded JSFiddle code at the bottom of this page.
The goal is to make each sphere selectable through clicking (or screen touching on mobile devices). Clicking on a sphere should make the camera zoom on the selected one and show the two parameters controlling it. Clicking on an empty zone in the viewer should make the camera zoom out again to show the whole scene.
The ShapeDiver model is prepared to facilitate the integration: each sphere is named according to an index i ("Sphere_{i}") and the parameters related to it are named with the same index ("Radius_{i}" and "Color_{i}").
Simple HTML page
We define a minimal HTML page containing:
- A container for the ShapeDiver viewer
- A container with one slider (for the radius of the selected) and one color input (for the color of the selected sphere). Depending on which sphere is selected, these inputs will trigger a different parameter in the ShapeDiver model.
- A placeholder container only displayed when no sphere is selected, which just contains a prompt "Select sphere..."
<body>
<!-- ShapeDiver Viewer Main Container -->
<div id='sdv-container' style="width:100%;height:300px;">
</div>
<div id="placeholder">
<p>Select a sphere...</p>
</div>
<div id="parameters" hidden>
<input type=range min=1 max=5 id="Radius" onchange="updateRadius(this.value);">
<label id="Radiuslabel" for="Radius">Radius of the selected sphere</label>
<br>
<input type=color id="Color" onchange="updateColor(this.value);">
<label id="Colorlabel" for="Color">Color of the selected sphere</label>
</div>
</body>
Initially, the parameters <div> is hidden since no spheres are selected, and the placeholder is shown instead.
The onchange event of each input is linked to a javascript function which we will define later. Essentially, the functions will determine which sphere is selected and request a parameter updated according to this information.
Defining the interaction group
The main part of this tutorial is to make each sphere selectable, and to define listeners for the selection events, where we will connect the inputs.
We decide to make the spheres both hoverable and clickable. First, we need to create a new interaction group in the viewer. We call this group spheres and define that objects within this group can be hovered and selected:
var sphereGroup = {
id: "spheres",
hoverable: true,
hoverEffect: hoverEffect,
selectable: true,
selectionEffect: selectionEffect
};
Then we define what happens when objects of the spheres groups are hovered and selected. This is done by creating interaction effects:
var hoverEffect = {
active: {
name: 'colorHighlight',
options: {
color: [100, 100, 100]
}
}
};
var selectionEffect = {
active: {
name: 'colorHighlight',
options: {
color: [255, 255, 255]
}
}
};
The hover effect creates a highlight with a light grey color ([100,100,100]) on the object, while the selection effect creates a whilte highlight ([255,255,255]). Those effects are included in the interaction group we defined earlier.
Finally, we need to add the interaction group to the scene, which is done with a simple call:
api.scene.updateInteractionGroups(sphereGroup);
Making the spheres selectable
Now that we have defined an interaction group doing what we want, we just need to add assets to this group, in our case the spheres. To this end, we use the updatePersistentAsync() function of the scene interface. For each sphere, we send the following update object:
let updateObject = {
id: asset.id,
duration: 0,
interactionGroup: sphereGroup.id,
};
It just tells the viewer which asset to update (id) and what to updated (which interactionGroup the asset belongs to, in this case the sphereGroup). The duration property defines if a transition effect should be displayed for the updated, and how long it takes. By setting the value 0, we ensure that the update will happen transparently: it won't be noticeable to the users.
In our example, we create an array of updateObjects and send all of them at once to the viewer:
api.scene.updatePersistentAsync(updateObjects, 'CommPlugin_1');
At this point, the spheres should already be interactive in the viewer. Of course, they don't trigger anything else yet.
Listening for the selection event
When a sphere is selected, we want to do a few things:
- Find which sphere was selected.
- Center and zoom the camera on the selected sphere.
- Display the slider and color inputs in the HTML page instead of the placeholder.
- Initialize the input values with the current parameter values for the selected sphere.
We set up an event listener for the SELECT_ON event of the scene interface. The callback to this event will take care of those two points.
api.scene.addEventListener(api.scene.EVENTTYPE.SELECT_ON, selectCallback);
Find which sphere was selected
The event response passed to the callback contains a scenePath property, which contains the full path in the scene of the selected object. A scene path is a string that always starts with "PLUGIN_ID.ASSET_ID", with optionally further dot-separated values describing with more detail partial asset elements. In our example, each sphere is contained in a different asset, therefore finding the ID of the selected sphere asset is easy. From the idea, we can get the asset and its name (Sphere_{i}), from which we finally get the index i of the selected sphere.
let sphereID = event.scenePath.split(".")[1];
let sphereAsset = api.scene.get({
id: sphereID
}, "CommPlugin_1");
selectedSphere = sphereAsset.data[0].name.split("_")[1];
Center and zoom the camera on the selected sphere
Centering and zooming the camera on a specific asset is done using its path in the scene, which we get from the event object. Therefore this step is done in a single line:
api.scene.camera.zoomAsync([event.scenePath],{duration:500});
The second argument of the ZoomAsync() function determines how long the camera movement from its current position to its target position will take. We set it to 500ms for a smooth camera updated.
Display the slider and color inputs in the HTML page instead of the placeholder
This is pretty standard DOM manipulation with javascript.
document.getElementById("placeholder").setAttribute("hidden", "");
document.getElementById("parameters").removeAttribute("hidden", "");
Initialize the input values
Here we have to connect the selected sphere with its corresponding parameters in the viewer. Since we already have the index i of the selected sphere, we can just read the values of the Radius_{i} and Color_{i} parameters which we know are defined in the model, and set the input values according to those.
// radius control
let val = api.parameters.get({name: "Radius_" + selectedSphere.toString()}).data[0].value;
document.getElementById("Radius").value = val;
// color control
val = api.parameters.get({name: "Color_" + selectedSphere.toString()}).data[0].value;
document.getElementById("Color").value = val.substring(0, val.length-2).replace("0x", "#");
Note that regarding the color parameter, we need to convert the value returned by the API, which is in "0xHHHHHHHH" form (including an alpha channel) to the HTML form "#HHHHHH".
Listening for the deselection event
Besides hiding the inputs and showing the placeholder again, we want to zoom back on the whole scene when spheres are deselected. We do so by giving a null value to the zoomAsync() function
api.scene.addEventListener(api.scene.EVENTTYPE.SELECT_OFF, function(event) {
document.getElementById("parameters").setAttribute("hidden", "");
document.getElementById("placeholder").removeAttribute("hidden", "");
api.scene.camera.zoomAsync(null,{duration:500});
});
Linking the HTML inputs with parameter updates
At this point, we display the correct parameters with the correct values when a sphere is selected. However, we still need to trigger the right parameter update when the inputs are modified. We do that in the update functions that are already linked with the inputs in the <input> element.
var updateRadius = function(val) {
let param = "Radius_" + selectedSphere.toString();
api.parameters.updateAsync({
name: param,
value: val
})
};
We also make the UX choice to deselect the sphere after the parameter is updated. This is done when the updateAsync() promise returns. Deselecting elements can be done by sending a null value to the updateSelected() function.
api.parameters.updateAsync({
name: param,
value: val
}).then(
function(response) {
api.scene.updateSelected(null,api.scene.getSelected());
}
);
Comments
0 comments
Article is closed for comments.