Skip to content

Commit 6a9c27b

Browse files
committed
initial scaffolding for volume trace
1 parent b931e4f commit 6a9c27b

File tree

8 files changed

+460
-0
lines changed

8 files changed

+460
-0
lines changed

lib/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ Plotly.register([
2727
require('./surface'),
2828
require('./mesh3d'),
2929
require('./cone'),
30+
require('./volume'),
3031

3132
require('./scattergeo'),
3233
require('./choropleth'),

lib/volume.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/**
2+
* Copyright 2012-2018, Plotly, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the MIT license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
'use strict';
10+
11+
module.exports = require('../src/traces/volume');

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@
8383
"gl-select-box": "^1.0.2",
8484
"gl-spikes2d": "^1.0.1",
8585
"gl-surface3d": "^1.3.5",
86+
"gl-volume3d": "https://github.com/gl-vis/gl-volume3d.git",
8687
"glslify": "^6.1.1",
8788
"has-hover": "^1.0.1",
8889
"has-passive-events": "^1.0.0",

src/traces/volume/attributes.js

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
/**
2+
* Copyright 2012-2018, Plotly, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the MIT license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
'use strict';
10+
11+
var colorAttrs = require('../../components/colorscale/color_attributes');
12+
var colorscaleAttrs = require('../../components/colorscale/attributes');
13+
var colorbarAttrs = require('../../components/colorbar/attributes');
14+
var mesh3dAttrs = require('../mesh3d/attributes');
15+
var baseAttrs = require('../../plots/attributes');
16+
17+
var extendFlat = require('../../lib/extend').extendFlat;
18+
19+
var attrs = {
20+
x: {
21+
valType: 'data_array',
22+
role: 'info',
23+
editType: 'calc+clearAxisTypes',
24+
description: [
25+
'Sets the x coordinates of the isosurface'
26+
].join(' ')
27+
},
28+
y: {
29+
valType: 'data_array',
30+
role: 'info',
31+
editType: 'calc+clearAxisTypes',
32+
description: [
33+
'Sets the y coordinates of the isosurface'
34+
].join(' ')
35+
},
36+
z: {
37+
valType: 'data_array',
38+
role: 'info',
39+
editType: 'calc+clearAxisTypes',
40+
description: [
41+
'Sets the z coordinates of the isosurface'
42+
].join(' ')
43+
},
44+
45+
u: {
46+
valType: 'data_array',
47+
editType: 'calc',
48+
description: 'Sets the intensity values of the isosurface.'
49+
},
50+
51+
imin: {
52+
valType: 'number',
53+
editType: 'calc',
54+
description: 'Sets the minimum iso bound of the isosurface.'
55+
},
56+
57+
imax: {
58+
valType: 'number',
59+
editType: 'calc',
60+
description: 'Sets the maximum iso bound of the isosurface.'
61+
},
62+
63+
smoothnormals: {
64+
valType: 'boolean',
65+
editType: 'calc',
66+
description: ''
67+
},
68+
69+
singlemesh: {
70+
valType: 'boolean',
71+
editType: 'calc',
72+
description: ''
73+
},
74+
75+
isocaps: {
76+
valType: 'boolean',
77+
editType: 'calc',
78+
description: ''
79+
},
80+
81+
boundmin: {
82+
valType: 'data_array',
83+
editType: 'calc',
84+
description: ''
85+
},
86+
87+
boundmax: {
88+
valType: 'data_array',
89+
editType: 'calc',
90+
description: ''
91+
},
92+
93+
text: {
94+
valType: 'string',
95+
role: 'info',
96+
dflt: '',
97+
arrayOk: true,
98+
editType: 'calc',
99+
description: [
100+
'Sets the text elements associated with the isosurface points.',
101+
'If trace `hoverinfo` contains a *text* flag and *hovertext* is not set,',
102+
'these elements will be seen in the hover labels.'
103+
].join(' ')
104+
}
105+
};
106+
107+
extendFlat(attrs, colorAttrs('', 'calc', true), {
108+
showscale: colorscaleAttrs.showscale,
109+
colorbar: colorbarAttrs
110+
});
111+
delete attrs.color;
112+
113+
var fromMesh3d = ['opacity', 'lightposition', 'lighting'];
114+
115+
fromMesh3d.forEach(function(k) {
116+
attrs[k] = mesh3dAttrs[k];
117+
});
118+
119+
attrs.hoverinfo = extendFlat({}, baseAttrs.hoverinfo, {
120+
editType: 'calc',
121+
flags: ['x', 'y', 'z', 'intensity', 'text', 'name'],
122+
dflt: 'x+y+z+intensity+text+name'
123+
});
124+
125+
module.exports = attrs;

src/traces/volume/calc.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/**
2+
* Copyright 2012-2018, Plotly, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the MIT license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
'use strict';
10+
11+
var colorscaleCalc = require('../../components/colorscale/calc');
12+
13+
module.exports = function calc(gd, trace) {
14+
var u = trace.u;
15+
var len = u.length;
16+
var normMax = -Infinity;
17+
var normMin = Infinity;
18+
19+
for(var i = 0; i < len; i++) {
20+
var uu = u[i];
21+
var norm = uu;
22+
23+
normMax = Math.max(normMax, norm);
24+
normMin = Math.min(normMin, norm);
25+
}
26+
27+
trace._normMax = normMax;
28+
29+
colorscaleCalc(trace, [normMin, normMax], '', 'c');
30+
};

src/traces/volume/convert.js

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
/**
2+
* Copyright 2012-2018, Plotly, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the MIT license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
/*
10+
Usage example:
11+
12+
var width = 64
13+
var height = 64
14+
var depth = 64
15+
16+
var xs = []
17+
var ys = []
18+
var zs = []
19+
20+
var data = new Uint16Array(width*height*depth)
21+
for (var z=0; z<depth; z++)
22+
for (var y=0; y<height; y++)
23+
for (var x=0; x<width; x++) {
24+
xs.push(x/width);
25+
ys.push(y/height);
26+
zs.push(z/depth);
27+
var value = 1500 + 500 * (
28+
Math.sin(2 * 2*Math.PI*(z/depth-0.5)) +
29+
Math.cos(3 * 2*Math.PI*(x/width-0.5)) +
30+
Math.sin(4 * 2*Math.PI*(y/height-0.5))
31+
);
32+
data[z*height*width + y*width + x] = value
33+
}
34+
35+
Plotly.newPlot(gd, [{
36+
type: 'volume',
37+
38+
x: xs,
39+
y: ys,
40+
z: zs,
41+
42+
u: data,
43+
44+
imin: 1600,
45+
imax: 2000,
46+
cmin: 1500,
47+
cmax: 2000,
48+
49+
isocaps: true,
50+
51+
colorscale: 'Portland'
52+
}], {
53+
scene: {
54+
xaxis: {range: [0, 1]},
55+
yaxis: {range: [0, 1]},
56+
zaxis: {range: [0, 1]}
57+
}
58+
})
59+
60+
*/
61+
'use strict';
62+
63+
var volumePlot = require('gl-volume3d');
64+
65+
var simpleMap = require('../../lib').simpleMap;
66+
var parseColorScale = require('../../lib/gl_format_color').parseColorScale;
67+
68+
function Volume(scene, uid) {
69+
this.scene = scene;
70+
this.uid = uid;
71+
this.mesh = null;
72+
this.data = null;
73+
}
74+
75+
var proto = Volume.prototype;
76+
77+
proto.handlePick = function(selection) {
78+
if(selection.object === this.mesh) {
79+
var selectIndex = selection.index = selection.data.index;
80+
81+
selection.traceCoordinate = [
82+
selection.data.position[0],
83+
selection.data.position[1],
84+
selection.data.position[2],
85+
selection.data.intensity
86+
];
87+
88+
var text = this.data.text;
89+
if(Array.isArray(text) && text[selectIndex] !== undefined) {
90+
selection.textLabel = text[selectIndex];
91+
} else if(text) {
92+
selection.textLabel = text;
93+
}
94+
95+
return true;
96+
}
97+
};
98+
99+
var axisName2scaleIndex = {xaxis: 0, yaxis: 1, zaxis: 2};
100+
101+
function getSequence(src) {
102+
var xs = [src[0]];
103+
for(var i = 1, last = xs[0]; i < src.length; i++) {
104+
var p = src[i];
105+
if(p >= last) {
106+
if(p > last) {
107+
xs.push(p);
108+
}
109+
last = p;
110+
} else {
111+
break;
112+
}
113+
}
114+
return xs;
115+
}
116+
117+
function convert(scene, trace) {
118+
var sceneLayout = scene.fullSceneLayout;
119+
var dataScale = scene.dataScale;
120+
var volumeOpts = {};
121+
122+
function toDataCoords(arr, axisName) {
123+
var ax = sceneLayout[axisName];
124+
var scale = dataScale[axisName2scaleIndex[axisName]];
125+
return simpleMap(arr, function(v) { return ax.d2l(v) * scale; });
126+
}
127+
128+
var xs = getSequence(trace.x);
129+
var ys = getSequence(trace.y);
130+
var zs = getSequence(trace.z);
131+
132+
volumeOpts.dimensions = [xs.length, ys.length, zs.length];
133+
volumeOpts.meshgrid = [
134+
toDataCoords(xs, 'xaxis'),
135+
toDataCoords(ys, 'yaxis'),
136+
toDataCoords(zs, 'zaxis')
137+
];
138+
139+
// var bounds = [
140+
// volumeOpts.boundmin || [xs[0], ys[0], zs[0]],
141+
// volumeOpts.boundmax || [xs[xs.length - 1], ys[ys.length - 1], zs[zs.length - 1]]
142+
// ];
143+
144+
145+
volumeOpts.values = trace.u;
146+
147+
volumeOpts.colormap = parseColorScale(trace.colorscale);
148+
149+
volumeOpts.vertexIntensityBounds = [trace.cmin, trace.cmax];
150+
volumeOpts.isoBounds = [trace.imin, trace.imax];
151+
152+
volumeOpts.isoCaps = trace.isocaps;
153+
154+
var bounds = [[0, 0, 0], volumeOpts.dimensions];
155+
156+
var meshData = volumePlot(volumeOpts, bounds);
157+
158+
// pass gl-mesh3d lighting attributes
159+
var lp = trace.lightposition;
160+
meshData.lightPosition = [lp.x, lp.y, lp.z];
161+
meshData.ambient = trace.lighting.ambient;
162+
meshData.diffuse = trace.lighting.diffuse;
163+
meshData.specular = trace.lighting.specular;
164+
meshData.roughness = trace.lighting.roughness;
165+
meshData.fresnel = trace.lighting.fresnel;
166+
meshData.opacity = trace.opacity;
167+
168+
return meshData;
169+
}
170+
171+
proto.update = function(data) {
172+
this.data = data;
173+
174+
var meshData = convert(this.scene, data);
175+
this.mesh.update(meshData);
176+
};
177+
178+
proto.dispose = function() {
179+
this.scene.glplot.remove(this.mesh);
180+
this.mesh.dispose();
181+
};
182+
183+
function createvolumeTrace(scene, data) {
184+
var gl = scene.glplot.gl;
185+
186+
var meshData = convert(scene, data);
187+
var mesh = volumePlot.createTriMesh(gl, meshData);
188+
189+
var volume = new Volume(scene, data.uid);
190+
volume.mesh = mesh;
191+
volume.data = data;
192+
volume.meshData = meshData;
193+
mesh._trace = volume;
194+
195+
scene.glplot.add(mesh);
196+
197+
return volume;
198+
}
199+
200+
module.exports = createvolumeTrace;

0 commit comments

Comments
 (0)