Details On Demand
Example demonstrating how to use the .action() method to add Babylon pointer actions and control Babylon GUI elements for details on demand hover interactions.
js
// SPDX-License-Identifier: Apache-2.0
// Copyright : J.P. Morgan Chase & Co.
import * as anu from '@jpmorganchase/anu';
import * as d3 from "d3";
import { Scene, HemisphericLight, ArcRotateCamera, Vector3, Color3, ActionManager, ExecuteCodeAction, HighlightLayer } from '@babylonjs/core';
import { AdvancedDynamicTexture, Rectangle, TextBlock} from '@babylonjs/gui'
import cars from './data/cars.json' assert {type: 'json'}; //Our data
//Create and export a function that takes a Babylon engine and returns a Babylon Scene
export function details(engine) {
//Create an empty Scene
const scene = new Scene(engine);
//Add some lighting
new HemisphericLight('light1', new Vector3(0, 10, 0), scene);
//Add a camera that rotates around the origin and adjust its properties
const camera = new ArcRotateCamera("Camera", -(Math.PI / 4) * 3, Math.PI / 4, 10, new Vector3(0, 0, 0), scene);
camera.wheelPrecision = 20; // Adjust the sensitivity of the mouse wheel's zooming
camera.minZ = 0; // Adjust the distance of the camera's near plane
camera.attachControl(true); // Allow the camera to respond to user controls
camera.position = new Vector3(2, 2, -3.5);
//Create the D3 scale functions for the x, y, and z positions and color
let scaleX = d3.scaleLinear().domain(d3.extent(d3.map(cars, (d) => { return d['Weight_in_lbs'] }))).range([-1, 1]).nice();
let scaleY = d3.scaleLinear().domain(d3.extent(d3.map(cars, (d) => { return d['Horsepower'] }))).range([-1, 1]).nice();
let scaleZ = d3.scaleLinear().domain(d3.extent(d3.map(cars, (d) => { return d['Acceleration'] }))).range([-1, 1]).nice();
let scaleC = d3.scaleOrdinal(anu.ordinalChromatic('d310').toStandardMaterial());
//Create a Center of Transform TransformNode using create() that serves the parent node for all our meshes that make up our chart
let CoT = anu.create("cot", "cot");
//We need to make an Anu Selection separately, as create() does not return a Section but the created Babylon TransformNode
let chart = anu.selectName("cot", scene);
//Create a Babylon HighlightLayer that will allow us to add a highlight stencil to meshes
const highlighter = new HighlightLayer("highlighter", scene);
//Create a plane mesh that will serve as the basis for our details on demand label
const hoverPlane = anu.create('plane', 'hoverPlane', {width: 1, height: 1})
hoverPlane.isPickable = false; //Disable picking so it doesn't get in the way of interactions
hoverPlane.renderingGroupId = 1; //Set render id higher so it always renders in front
//Use the Babylon GUI system to create an AdvancedDynamicTexture that will the updated with our label content
let advancedTexture = AdvancedDynamicTexture.CreateForMesh(hoverPlane);
//Create a rectangle for the background
let UIBackground = new Rectangle();
UIBackground.adaptWidthToChildren = true;
UIBackground.adaptHeightToChildren = true;
UIBackground.cornerRadius = 20;
UIBackground.color = "Black";
UIBackground.thickness = 2;
UIBackground.background = "White";
advancedTexture.addControl(UIBackground);
//Create an empty text block
let label = new TextBlock();
label.paddingLeftInPixels = 25;
label.paddingRightInPixels = 25;
label.fontSizeInPixels = 50;
label.resizeToFit = true;
label.text = " "
UIBackground.addControl(label);
//Hide the plane until it is needed
hoverPlane.isVisible = false;
//Set billboard mode to always face camera
hoverPlane.billboardMode = 7;
//Create our spheres and add Actions to allow them to respond to user input
let spheres = chart.bind('sphere', { diameter: 0.05 }, cars)
.position((d) => new Vector3(scaleX(d['Weight_in_lbs']), scaleY(d['Horsepower']), scaleZ(d['Acceleration'])))
.material((d) => scaleC(d['Origin']))
//Add an action that will highlight the sphere mesh using the highlight stencil when the pointer is moved over it,
//as well as show and properly position the hoverPlane above the sphere mesh
.action((d,n,i) => new ExecuteCodeAction( //Type of action, ExecudeCodeAction allows us to execute a given function
ActionManager.OnPointerOverTrigger, //Action Trigger
() => {
highlighter.addMesh(n, Color3.White());
//Show and adjust the label
hoverPlane.isVisible = true;
label.text = d['Name'];
hoverPlane.position = n.position.add(new Vector3(0, 0.1, 0)); //Add vertical offset
}
))
//Add an action that will undo the above when the pointer is moved away from the sphere mesh
.action((d,n,i) => new ExecuteCodeAction(
ActionManager.OnPointerOutTrigger,
() => {
highlighter.removeMesh(n);
hoverPlane.isVisible = false;
}
))
//Use the createAxes() Anu helper function to create the axes for us based on our D3 scale functions
anu.createAxes('test', scene, { parent: chart, scale: { x: scaleX, y: scaleY, z: scaleZ }, labelOptions: {size: 0.02} });
return scene;
};