@@ -7,7 +7,7 @@ import * as d3 from 'd3';
77import data from './data/penguins.json' ;
88
99//Create and export a function that takes a Babylon engine and returns a Babylon Scene
10- export function violinChart ( engine ) {
10+ export function violinPlot ( engine ) {
1111
1212 //Create an empty Scene
1313 const scene = new BABYLON . Scene ( engine ) ;
@@ -16,70 +16,66 @@ export function violinChart(engine) {
1616 fillLight . intensity = 1.25 ;
1717 fillLight . groundColor = new BABYLON . Color3 ( 0.5 , 0.5 , 0.5 ) ;
1818 //Add a camera that rotates around the origin and adjust its properties
19- const camera = new BABYLON . ArcRotateCamera ( 'Camera' , 0 , 0 , 0 , new BABYLON . Vector3 ( 0 , 0.5 , 0 ) , scene ) ;
20- camera . position = new BABYLON . Vector3 ( 0 , 0.5 , - 3 ) ;
19+ const camera = new BABYLON . ArcRotateCamera ( 'Camera' , 0 , 0 , 0 , new BABYLON . Vector3 ( 0 , 0.75 , 0 ) , scene ) ;
20+ camera . position = new BABYLON . Vector3 ( 0 , 0.75 , - 2.5 ) ;
2121 camera . wheelPrecision = 20 ;
2222 camera . minZ = 0 ;
2323 camera . attachControl ( true ) ;
2424
2525 // Kernel density estimation functions (adapted from D3 gallery example)
2626 function kernelDensityEstimator ( kernel , ticks ) {
27- return function ( values ) {
28- return ticks . map ( function ( x ) {
29- return [ x , d3 . mean ( values , function ( v ) { return kernel ( x - v ) ; } ) ] ;
27+ return function ( values ) {
28+ return ticks . map ( function ( x ) {
29+ return [
30+ x ,
31+ d3 . mean ( values , function ( v ) {
32+ return kernel ( x - v ) ;
33+ } ) ,
34+ ] ;
3035 } ) ;
3136 } ;
3237 }
33-
38+
3439 function kernelEpanechnikov ( bandwidth ) {
35- return function ( v ) {
36- return Math . abs ( v /= bandwidth ) <= 1 ? 0.75 * ( 1 - v * v ) / bandwidth : 0 ;
40+ return function ( v ) {
41+ return Math . abs ( ( v /= bandwidth ) ) <= 1 ? ( 0.75 * ( 1 - v * v ) ) / bandwidth : 0 ;
3742 } ;
3843 }
3944
4045 // Get unique species
41- const species = Array . from ( new Set ( data . map ( d => d . Species ) ) ) ;
42-
46+ const species = Array . from ( new Set ( data . map ( ( d ) => d . Species ) ) ) ;
47+
4348 // Create scales for the visualization
4449 // Y scale for the beak length values
45- const beakLengths = data . map ( d => d [ 'Beak Length (mm)' ] ) . filter ( d => d != null ) ;
46- const scaleY = d3 . scaleLinear ( )
47- . domain ( d3 . extent ( beakLengths ) )
48- . range ( [ 0 , 1.5 ] )
49- . nice ( ) ;
50-
50+ const beakLengths = data . map ( ( d ) => d [ 'Beak Length (mm)' ] ) . filter ( ( d ) => d != null ) ;
51+ const scaleY = d3 . scaleLinear ( ) . domain ( d3 . extent ( beakLengths ) ) . range ( [ 0 , 1.5 ] ) . nice ( ) ;
52+
5153 // X scale for positioning the violins (one for each species)
52- const scaleX = d3 . scaleBand ( )
53- . domain ( species )
54- . range ( [ - 1 , 1 ] )
55- . paddingInner ( 1 ) . paddingOuter ( 0.5 ) ;
56-
54+ const scaleX = d3 . scaleBand ( ) . domain ( species ) . range ( [ - 1 , 1 ] ) . paddingInner ( 1 ) . paddingOuter ( 0.5 ) ;
5755
5856 // Create kernel density estimator
5957 const kde = kernelDensityEstimator ( kernelEpanechnikov ( 0.7 ) , scaleY . ticks ( 50 ) ) ;
60-
58+
6159 // Compute density for each species
62- const densityData = species . map ( sp => {
60+ const densityData = species . map ( ( sp ) => {
6361 const values = data
64- . filter ( d => d . Species === sp && d [ 'Beak Length (mm)' ] != null )
65- . map ( d => d [ 'Beak Length (mm)' ] ) ;
62+ . filter ( ( d ) => d . Species === sp && d [ 'Beak Length (mm)' ] != null )
63+ . map ( ( d ) => d [ 'Beak Length (mm)' ] ) ;
6664 return {
6765 species : sp ,
68- density : kde ( values )
66+ density : kde ( values ) ,
6967 } ;
7068 } ) ;
7169
7270 // Find the maximum density value across all species for scaling
7371 let maxDensity = 0 ;
74- densityData . forEach ( d => {
75- const max = d3 . max ( d . density , v => v [ 1 ] ) ;
72+ densityData . forEach ( ( d ) => {
73+ const max = d3 . max ( d . density , ( v ) => v [ 1 ] ) ;
7674 if ( max > maxDensity ) maxDensity = max ;
7775 } ) ;
7876
7977 // Scale for the width of the violins based on density
80- const scaleWidth = d3 . scaleLinear ( )
81- . domain ( [ 0 , maxDensity ] )
82- . range ( [ 0 , 0.25 ] ) ; // 0.25 to leave some space between violins
78+ const scaleWidth = d3 . scaleLinear ( ) . domain ( [ 0 , maxDensity ] ) . range ( [ 0 , 0.25 ] ) ; // 0.25 to leave some space between violins
8379
8480 // Color scale for different species using Anu helper function
8581 const scaleC = d3 . scaleOrdinal ( anu . ordinalChromatic ( 'd310' ) . toColor3 ( ) ) ;
@@ -91,50 +87,36 @@ export function violinChart(engine) {
9187 // Function to create the path array for a violin shape
9288 // All violins are created at x=0 and will be positioned later
9389 const createViolinPath = ( speciesData ) => {
90+
9491 // Create the path for the violin shape (right half) - from bottom to top
95- const rightPath = speciesData . density . map ( d =>
96- new BABYLON . Vector3 ( scaleWidth ( d [ 1 ] ) , scaleY ( d [ 0 ] ) , 0 )
97- ) ;
98-
92+ const rightPath = speciesData . density . map ( ( d ) => new BABYLON . Vector3 ( scaleWidth ( d [ 1 ] ) , scaleY ( d [ 0 ] ) , 0 ) ) ;
93+
9994 // Create the left half (mirror of right half) - from top to bottom
100- const leftPath = speciesData . density . map ( d =>
101- new BABYLON . Vector3 ( - scaleWidth ( d [ 1 ] ) , scaleY ( d [ 0 ] ) , 0 )
102- ) . reverse ( ) ;
103-
95+ const leftPath = speciesData . density . map ( ( d ) => new BABYLON . Vector3 ( - scaleWidth ( d [ 1 ] ) , scaleY ( d [ 0 ] ) , 0 ) ) . reverse ( ) ;
96+
10497 // Combine both halves to create a closed shape
10598 const fullPath = [ ...rightPath , ...leftPath ] ;
106-
99+
107100 // Smooth the path for better visual appearance
108101 const smoothPath = BABYLON . Curve3 . CreateCatmullRomSpline ( fullPath , 30 , true ) . getPoints ( ) ;
109-
102+
110103 // Create center line path at the same points for ribbon fill
111- const centerPath = smoothPath . map ( p => new BABYLON . Vector3 ( 0 , p . y , 0 ) ) ;
112-
104+ const centerPath = smoothPath . map ( ( p ) => new BABYLON . Vector3 ( 0 , p . y , 0 ) ) ;
105+
113106 // Return the path array for ribbon creation - from center to outline
114107 return [ centerPath , smoothPath ] ;
115108 } ;
116109
117110 // Create violin shapes using anu.bind for each species
118- let violins = chart . bind ( 'ribbon' ,
119- {
120- pathArray : ( d ) => createViolinPath ( d ) ,
121- closeArray : true ,
122- closePath : true ,
123- sideOrientation : BABYLON . Mesh . DOUBLESIDE
124- } ,
125- densityData )
111+ let violins = chart . bind ( 'ribbon' , { pathArray : ( d ) => createViolinPath ( d ) , sideOrientation : BABYLON . Mesh . DOUBLESIDE } , densityData )
126112 . positionX ( ( d ) => scaleX ( d . species ) )
127113 . positionZ ( - 0.01 ) // Slightly adjust z to prevent Z-fighting
128114 . material ( ( d , n , i ) => new BABYLON . StandardMaterial ( 'violinMaterial_' + i , scene ) )
129115 . diffuseColor ( ( d ) => scaleC ( d . species ) ) ;
130-
131116
132117 // Create axes with custom labels
133- anu . createAxes ( 'myAxes' , {
134- scale : {
135- x : scaleX ,
136- y : scaleY
137- } ,
118+ anu . createAxes ( 'myAxes' , {
119+ scale : { x : scaleX , y : scaleY } ,
138120 parent : chart ,
139121 } ) ;
140122
0 commit comments