DataVisual is a design/programming pattern for developing data visualizations with WebGL 3D assets.
The pattern is implemented in JavaScript using the Three.js framework, but is applicable to other frameworks like Babylon.
With a DataVisual you can dynamically visualize individual meshes, that have unique materials associated with them, in a Three.js scene.
Traditional dataset processing can be used on a DataVisual leveraging JavaScript's robust array methods; most notably MapReduce patterns.
Hello DataVisual demo (source) is available on Observable and in the repository.
The demonstration illustrates DataVisual's features with a use case simulating a U.S. Navy (Go Navy! Beat Army) aquaBot determining an optimal salvage retrieval strategy for objects floating and submersed out at sea.
aquaBot utilizes information about the salvage items in a data array partnered (joined) with the 3D visual's spatial attributes to maximize the salvage with a fixed amount of fuel.
The 3D visual was developed with Blender and exported as a glTF file. Three.js's GLTFLoader is used in the demonstration to load the 3D visual and then dynamically visualize it.
The visualization animates the salvage strategy and leverages several of DataVisual's features including:
- Maintaining join context when sorting the join object;
- Referencing joined properties from both the data and visual;
- Dynamic data coloring/visualization using D3.js.
- Rendering only objects that will be salvaged with a data-driven algorithm;
- All objects can be displayed/rendered with a user-driven (aquaBot's operator) option for human validation;
- Use of the built-in tooltip and mouse event handler.
If using the Chrome browser with file system access, the --allow-file-access-from-files flag must be used.
See: How to set the allow-file-access-from-files flag option in Google Chrome
Download zip of code, unzip to a folder and launch index.html from a web server or from the file system with a WebGL enabled browser.
The demonstration application will intially prompt for the type of glTF file to utilize:
- gltf/seaScape.gltf utilizes data as a separate object in the join.
- gltf/seaScapeEmbeddedData.gltf utilizes embedded data within the glTF file; extracting it first before implementing the join.
Several Observable Notebooks in the DataVisual Collection.
Assuming the following:
- A JavaScript data object in the following format:
const data = [{name: "object1", value: value1, ...} ,{name: "object2", value2, ...}, ... }];
- A glTF file loaded into the variable visual with name attibutes for the meshes that match the name attributes for the data object;
Create a DataVisual object with the following syntax:
const datVisual = new dataVisual();
datVisual.joinDataToVisual(data, visual);
// Alternate instantiation pattern supported by the ES6 class:
// const datVisual = new dataVisual({data, visual});
Three.js, as well as the glTF specifications, provide for a strategy to extend the visual with data that can be visualized:
Instantiating a DataVisual in the following format creates a dataVisual object from a visual when the data is embedded in the userData property.
const datVisual = new dataVisual();
datVisual.joinDataToVisual(visual);
// Alternate instantiation pattern supported by the ES6 class:
// const datVisual = new dataVisual({visual});
The dataVisual has the following methods and properties:
Methods:
Method | Description |
---|---|
.joinDataToVisual(data, visual) | Join data to a visual's mesh utilizing each object's name property |
.joinDataToVisual(data, visual, dataKey, visualKey) | Join data to a visual utilizing data's dataKey property and visual's visualKey property. visualKey can be a direct property of a mesh, or its .userData property. |
.joinDataToVisual(visual) | Creates a dataVisual object from a visual when the data is embedded within the userData property for mesh(es). |
.getJoinByUUID(uuid) | Get a joined row by referencing a mesh's uuid. This method is very helpful with raycasting techniques. |
.getJoinByUUID(uuid, "index") | Get a joined row's index by referencing a mesh's uuid. This method is very helpful with raycasting techniques. |
.getJoinByKey(dataKey) | Get a joined row by referencing a dataRow's dataKey. |
.getJoinByKey(dataKey, "index") | Get a joined row's index by referencing a dataRow's dataKey. |
.getDataColumn(column) | Get an array of a specified column (attribute/property) form the dataVisual's data. |
.setColorVisualObj(visualObj,color) | Set the material.color property for a mesh. The method utilizes the Three.js traverse method to set color for any children the mesh may have as well. |
.setColorByJoinIndex(index,color) | Set the material.color property for a mesh by referencing the index of the join. The method invokes .setColorVisualObj for the given index. |
.isolateObjects(visualObjs) | Isolates makes solely the visual objects in the visualObjs array visible; visible = true; |
.showAll() | Shows all visual objects in the DataVisual visible; visible = true; |
.addToolTip(renderer, camera[, getToolTipText]) | Implements a custom tooltip assuming the following parameters: (See Hello, DataVisual! for an example.) renderer: A Three.js WebGLRenderer object that has been attached to an HTML parent element. camera: A Three.js camera object getToolTipText: An optional tooltip text callback function that will be passed two parameters: references to the join row and the dataVisual instance itself. With the second parameter (dataVisual) the callback has access to all the object's properties. The getToolTipText callback function must return a DOMString that will be assigned to the tooltip's innerHTML property. |
.onHover(renderer, camera, handleEnter [,handleLeave]) | Implements mouse hover handlers for joined objects. Both handleEnter and the optional handleLeave callback funtions will be will be passed two parameters: references to the join row and the dataVisual instance itself. With the second (dataVisual) parameter the callback has access to all the object's properties. |
.onDoublClick(renderer, camera, handleDblClick) | Implements a mouse double-click event handler for joined objects. The handleDblClick callback function/handler will be passed two parameters: references to the join row and the dataVisual instance itself. With the second (dataVisual) parameter the callback has access to all the object's properties. |
Properties:
Property | Description |
---|---|
.join | A join object (array). Each element of the array will be an object referencing a joined (matching) dataRow and visuaObj. |
.join[n].dataRow | A reference to nth data row from the original data array that corresponds to the joined nth (matching) visualObj. |
.join[n].visualObj | A reference to the nth Three.js mesh being 'joined' to that corresponds to the joined nth (matching) dataRow. |
.data | The original data participating in the 'join'. |
.visual | The original visual participating in the 'join'. |
.dataKey | The data key attribute being used to join to a corresponding mesh in the visual. |
.visualKey | The visual key attribute being used to join to the visual's mesh. Property visualKey can be a direct property of a mesh or a property of the mesh's .userData member. |
.nonMatchingDataKeys | An array holding any dataKey(s) that did not join/match when the joinDataToVisual method was invoked. |
- D3.js - D3 framework
- Three.js - Three.js framework
- glTF - Khronos' graphic library Transmission Format
- GLTFLoader - A loader for glTF 2.0 resources
- dat.gui - dat.gui
- Blender - For building a 3D visual and exporting it to a glTF file.
- Mario Delgado Github: MarioDelgadoSr
- LinkedIn: Mario Delgado
- My Data Visualizer: A data visualization application using the DataVisual design pattern.
Free to all non-profit organizations. Businesses can email for licences details.