1+ // SPDX-License-Identifier: Apache-2.0
2+ // Copyright : J.P. Morgan Chase & Co.
3+
4+ import * as anu from '@jpmorganchase/anu' ;
5+ import * as BABYLON from '@babylonjs/core' ;
6+ import * as d3 from 'd3' ;
7+ import data from './data/stocks.csv' ;
8+
9+ //Create and export a function that takes a Babylon engine and returns a Babylon Scene
10+ export function linechart2DTubes ( engine ) {
11+
12+ //Create an empty Scene
13+ const scene = new BABYLON . Scene ( engine ) ;
14+ //Add some lighting
15+ new BABYLON . HemisphericLight ( 'light1' , new BABYLON . Vector3 ( 0 , 10 , - 5 ) , scene ) ;
16+ //Add a camera that rotates around the origin and adjust its properties
17+ const camera = new BABYLON . ArcRotateCamera ( 'Camera' , 0 , 0 , 0 , new BABYLON . Vector3 ( 0 , 0 , 0 ) , scene ) ;
18+ camera . position = new BABYLON . Vector3 ( 0 , 0 , - 3 ) ;
19+ camera . wheelPrecision = 20 ;
20+ camera . minZ = 0 ;
21+ camera . attachControl ( true ) ;
22+
23+ //Create D3 functions to parse the time and date
24+ let parseTime = d3 . timeParse ( '%b %d %Y' ) ;
25+ let dateFormat = d3 . timeFormat ( '%Y' ) ;
26+
27+ //Create the D3 functions that we will use to scale our data dimensions to desired output ranges for our visualization
28+ let scaleX = d3 . scaleTime ( ) . domain ( d3 . extent ( data . map ( ( d ) => parseTime ( d . date ) ) ) ) . range ( [ - 1 , 1 ] ) ;
29+ let scaleY = d3 . scaleLinear ( ) . domain ( [ 0 , Math . max ( ...data . map ( d => d . price ) ) ] ) . range ( [ - 1 , 1 ] ) . nice ( ) ;
30+ //Do the same for color, using Anu helper functions map scale outputs to Color3 objects based on the 'schemecategory10' palette from D3
31+ let scaleC = d3 . scaleOrdinal ( anu . ordinalChromatic ( 'd310' ) . toColor3 ( ) ) ;
32+
33+ //Create an array of arrays where each sub-array is an ordered list of Vector3 corresponding to the timeseries for each stock symbol
34+ //These Vectors will be the actual positional values for our lines
35+ let paths = Object . values ( data . reduce ( ( acc , d ) => {
36+ let position = new BABYLON . Vector3 ( scaleX ( parseTime ( d . date ) ) , scaleY ( d . price ) , 0 ) ;
37+ ( acc [ d . symbol ] = acc [ d . symbol ] || [ ] ) . push ( position ) ;
38+ return acc ;
39+ } , { } ) ) ;
40+
41+ //Create a Center of Transform TransformNode that serves the parent node for all our meshes that make up our chart
42+ let CoT = anu . create ( 'cot' , 'cot' ) ;
43+ //Select our CoT so that we have it as a Selection object
44+ let chart = anu . selectName ( 'cot' , scene ) ;
45+
46+ //Create tubes as children of our CoT that will render the paths we had defined, one tube for each path
47+ let lines = chart . bind ( 'tube' , { path : ( d ) => d , radius : 0.0075 } , paths )
48+ . material ( ( d , n , i ) => {
49+ //Set a material for each tube which is what determines its color
50+ const material = new BABYLON . StandardMaterial ( 'LineMaterial' + i ) ;
51+ material . diffuseColor = scaleC ( i ) ; //Set base color
52+ material . emissiveColor = scaleC ( i ) . multiplyByFloats ( 0.25 , 0.25 , 0.25 ) ; //Make a bit brighter, using full values will blow out the color
53+ material . specularColor = BABYLON . Color3 . Black ( ) ; //Disable reflections
54+ return material ;
55+ } ) ;
56+
57+ //Use the axes prefab with our two D3 scales with additional customizations
58+ anu . createAxes ( 'myAxes' , { scale : { x : scaleX , y : scaleY } ,
59+ parent : CoT ,
60+ domainMaterialOptions : { width : 0.01 } ,
61+ labelTicks : { x : scaleX . ticks ( d3 . timeYear ) } ,
62+ labelFormat : { x : dateFormat , y : ( text ) => '$' + text }
63+ } ) ;
64+
65+ return scene ;
66+ }
0 commit comments