From 6a9c27b7eca42b45a0e17883cf1012497e7b8e26 Mon Sep 17 00:00:00 2001 From: Ilmari Heikkinen Date: Sat, 23 Jun 2018 16:43:14 +0800 Subject: [PATCH 01/20] initial scaffolding for volume trace --- lib/index.js | 1 + lib/volume.js | 11 ++ package.json | 1 + src/traces/volume/attributes.js | 125 ++++++++++++++++++++ src/traces/volume/calc.js | 30 +++++ src/traces/volume/convert.js | 200 ++++++++++++++++++++++++++++++++ src/traces/volume/defaults.js | 57 +++++++++ src/traces/volume/index.js | 35 ++++++ 8 files changed, 460 insertions(+) create mode 100644 lib/volume.js create mode 100644 src/traces/volume/attributes.js create mode 100644 src/traces/volume/calc.js create mode 100644 src/traces/volume/convert.js create mode 100644 src/traces/volume/defaults.js create mode 100644 src/traces/volume/index.js diff --git a/lib/index.js b/lib/index.js index 4d014421b61..2b975cebd62 100644 --- a/lib/index.js +++ b/lib/index.js @@ -27,6 +27,7 @@ Plotly.register([ require('./surface'), require('./mesh3d'), require('./cone'), + require('./volume'), require('./scattergeo'), require('./choropleth'), diff --git a/lib/volume.js b/lib/volume.js new file mode 100644 index 00000000000..59f8579d548 --- /dev/null +++ b/lib/volume.js @@ -0,0 +1,11 @@ +/** +* Copyright 2012-2018, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +module.exports = require('../src/traces/volume'); diff --git a/package.json b/package.json index 7ff361e57c1..9046f8bb8fa 100644 --- a/package.json +++ b/package.json @@ -83,6 +83,7 @@ "gl-select-box": "^1.0.2", "gl-spikes2d": "^1.0.1", "gl-surface3d": "^1.3.5", + "gl-volume3d": "https://github.com/gl-vis/gl-volume3d.git", "glslify": "^6.1.1", "has-hover": "^1.0.1", "has-passive-events": "^1.0.0", diff --git a/src/traces/volume/attributes.js b/src/traces/volume/attributes.js new file mode 100644 index 00000000000..988483f3f8b --- /dev/null +++ b/src/traces/volume/attributes.js @@ -0,0 +1,125 @@ +/** +* Copyright 2012-2018, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var colorAttrs = require('../../components/colorscale/color_attributes'); +var colorscaleAttrs = require('../../components/colorscale/attributes'); +var colorbarAttrs = require('../../components/colorbar/attributes'); +var mesh3dAttrs = require('../mesh3d/attributes'); +var baseAttrs = require('../../plots/attributes'); + +var extendFlat = require('../../lib/extend').extendFlat; + +var attrs = { + x: { + valType: 'data_array', + role: 'info', + editType: 'calc+clearAxisTypes', + description: [ + 'Sets the x coordinates of the isosurface' + ].join(' ') + }, + y: { + valType: 'data_array', + role: 'info', + editType: 'calc+clearAxisTypes', + description: [ + 'Sets the y coordinates of the isosurface' + ].join(' ') + }, + z: { + valType: 'data_array', + role: 'info', + editType: 'calc+clearAxisTypes', + description: [ + 'Sets the z coordinates of the isosurface' + ].join(' ') + }, + + u: { + valType: 'data_array', + editType: 'calc', + description: 'Sets the intensity values of the isosurface.' + }, + + imin: { + valType: 'number', + editType: 'calc', + description: 'Sets the minimum iso bound of the isosurface.' + }, + + imax: { + valType: 'number', + editType: 'calc', + description: 'Sets the maximum iso bound of the isosurface.' + }, + + smoothnormals: { + valType: 'boolean', + editType: 'calc', + description: '' + }, + + singlemesh: { + valType: 'boolean', + editType: 'calc', + description: '' + }, + + isocaps: { + valType: 'boolean', + editType: 'calc', + description: '' + }, + + boundmin: { + valType: 'data_array', + editType: 'calc', + description: '' + }, + + boundmax: { + valType: 'data_array', + editType: 'calc', + description: '' + }, + + text: { + valType: 'string', + role: 'info', + dflt: '', + arrayOk: true, + editType: 'calc', + description: [ + 'Sets the text elements associated with the isosurface points.', + 'If trace `hoverinfo` contains a *text* flag and *hovertext* is not set,', + 'these elements will be seen in the hover labels.' + ].join(' ') + } +}; + +extendFlat(attrs, colorAttrs('', 'calc', true), { + showscale: colorscaleAttrs.showscale, + colorbar: colorbarAttrs +}); +delete attrs.color; + +var fromMesh3d = ['opacity', 'lightposition', 'lighting']; + +fromMesh3d.forEach(function(k) { + attrs[k] = mesh3dAttrs[k]; +}); + +attrs.hoverinfo = extendFlat({}, baseAttrs.hoverinfo, { + editType: 'calc', + flags: ['x', 'y', 'z', 'intensity', 'text', 'name'], + dflt: 'x+y+z+intensity+text+name' +}); + +module.exports = attrs; diff --git a/src/traces/volume/calc.js b/src/traces/volume/calc.js new file mode 100644 index 00000000000..4d90bbed482 --- /dev/null +++ b/src/traces/volume/calc.js @@ -0,0 +1,30 @@ +/** +* Copyright 2012-2018, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var colorscaleCalc = require('../../components/colorscale/calc'); + +module.exports = function calc(gd, trace) { + var u = trace.u; + var len = u.length; + var normMax = -Infinity; + var normMin = Infinity; + + for(var i = 0; i < len; i++) { + var uu = u[i]; + var norm = uu; + + normMax = Math.max(normMax, norm); + normMin = Math.min(normMin, norm); + } + + trace._normMax = normMax; + + colorscaleCalc(trace, [normMin, normMax], '', 'c'); +}; diff --git a/src/traces/volume/convert.js b/src/traces/volume/convert.js new file mode 100644 index 00000000000..3875139f7f9 --- /dev/null +++ b/src/traces/volume/convert.js @@ -0,0 +1,200 @@ +/** +* Copyright 2012-2018, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +/* + Usage example: + + var width = 64 + var height = 64 + var depth = 64 + + var xs = [] + var ys = [] + var zs = [] + + var data = new Uint16Array(width*height*depth) + for (var z=0; z= last) { + if(p > last) { + xs.push(p); + } + last = p; + } else { + break; + } + } + return xs; +} + +function convert(scene, trace) { + var sceneLayout = scene.fullSceneLayout; + var dataScale = scene.dataScale; + var volumeOpts = {}; + + function toDataCoords(arr, axisName) { + var ax = sceneLayout[axisName]; + var scale = dataScale[axisName2scaleIndex[axisName]]; + return simpleMap(arr, function(v) { return ax.d2l(v) * scale; }); + } + + var xs = getSequence(trace.x); + var ys = getSequence(trace.y); + var zs = getSequence(trace.z); + + volumeOpts.dimensions = [xs.length, ys.length, zs.length]; + volumeOpts.meshgrid = [ + toDataCoords(xs, 'xaxis'), + toDataCoords(ys, 'yaxis'), + toDataCoords(zs, 'zaxis') + ]; + + // var bounds = [ + // volumeOpts.boundmin || [xs[0], ys[0], zs[0]], + // volumeOpts.boundmax || [xs[xs.length - 1], ys[ys.length - 1], zs[zs.length - 1]] + // ]; + + + volumeOpts.values = trace.u; + + volumeOpts.colormap = parseColorScale(trace.colorscale); + + volumeOpts.vertexIntensityBounds = [trace.cmin, trace.cmax]; + volumeOpts.isoBounds = [trace.imin, trace.imax]; + + volumeOpts.isoCaps = trace.isocaps; + + var bounds = [[0, 0, 0], volumeOpts.dimensions]; + + var meshData = volumePlot(volumeOpts, bounds); + + // pass gl-mesh3d lighting attributes + var lp = trace.lightposition; + meshData.lightPosition = [lp.x, lp.y, lp.z]; + meshData.ambient = trace.lighting.ambient; + meshData.diffuse = trace.lighting.diffuse; + meshData.specular = trace.lighting.specular; + meshData.roughness = trace.lighting.roughness; + meshData.fresnel = trace.lighting.fresnel; + meshData.opacity = trace.opacity; + + return meshData; +} + +proto.update = function(data) { + this.data = data; + + var meshData = convert(this.scene, data); + this.mesh.update(meshData); +}; + +proto.dispose = function() { + this.scene.glplot.remove(this.mesh); + this.mesh.dispose(); +}; + +function createvolumeTrace(scene, data) { + var gl = scene.glplot.gl; + + var meshData = convert(scene, data); + var mesh = volumePlot.createTriMesh(gl, meshData); + + var volume = new Volume(scene, data.uid); + volume.mesh = mesh; + volume.data = data; + volume.meshData = meshData; + mesh._trace = volume; + + scene.glplot.add(mesh); + + return volume; +} + +module.exports = createvolumeTrace; diff --git a/src/traces/volume/defaults.js b/src/traces/volume/defaults.js new file mode 100644 index 00000000000..8396a64c2f8 --- /dev/null +++ b/src/traces/volume/defaults.js @@ -0,0 +1,57 @@ +/** +* Copyright 2012-2018, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var Lib = require('../../lib'); + +var colorscaleDefaults = require('../../components/colorscale/defaults'); +var attributes = require('./attributes'); + +module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) { + function coerce(attr, dflt) { + return Lib.coerce(traceIn, traceOut, attributes, attr, dflt); + } + + var u = coerce('u'); + + var x = coerce('x'); + var y = coerce('y'); + var z = coerce('z'); + + if( + !u || !u.length || + !x || !x.length || !y || !y.length || !z || !z.length + ) { + traceOut.visible = false; + return; + } + + coerce('imin'); + coerce('imax'); + coerce('smoothnormals'); + coerce('isocaps'); + // coerce('singlemesh'); + + coerce('lighting.ambient'); + coerce('lighting.diffuse'); + coerce('lighting.specular'); + coerce('lighting.roughness'); + coerce('lighting.fresnel'); + coerce('lightposition.x'); + coerce('lightposition.y'); + coerce('lightposition.z'); + + colorscaleDefaults(traceIn, traceOut, layout, coerce, {prefix: '', cLetter: 'c'}); + + coerce('text'); + + // disable 1D transforms (for now) + traceOut._length = null; +}; diff --git a/src/traces/volume/index.js b/src/traces/volume/index.js new file mode 100644 index 00000000000..65246c1d892 --- /dev/null +++ b/src/traces/volume/index.js @@ -0,0 +1,35 @@ +/** +* Copyright 2012-2018, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +module.exports = { + moduleType: 'trace', + name: 'volume', + basePlotModule: require('../../plots/gl3d'), + categories: ['gl3d'], + + attributes: require('./attributes'), + supplyDefaults: require('./defaults'), + colorbar: { + min: 'cmin', + max: 'cmax' + }, + calc: require('./calc'), + plot: require('./convert'), + + meta: { + description: [ + 'Use volumes to visualize volumetric data.', + '', + 'Specify a volume using 4 1D arrays,', + '3 position arrays `x`, `y` and `z`', + 'and intensity array `u`.' + ].join(' ') + } +}; From 31fbc9752554053d9e12655dcbbd045300202378 Mon Sep 17 00:00:00 2001 From: Ilmari Heikkinen Date: Sat, 23 Jun 2018 17:29:56 +0800 Subject: [PATCH 02/20] volume convert, pass gl to volume generator --- src/traces/volume/convert.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/traces/volume/convert.js b/src/traces/volume/convert.js index 3875139f7f9..499fe97d4ba 100644 --- a/src/traces/volume/convert.js +++ b/src/traces/volume/convert.js @@ -114,7 +114,7 @@ function getSequence(src) { return xs; } -function convert(scene, trace) { +function convert(gl, scene, trace) { var sceneLayout = scene.fullSceneLayout; var dataScale = scene.dataScale; var volumeOpts = {}; @@ -153,7 +153,7 @@ function convert(scene, trace) { var bounds = [[0, 0, 0], volumeOpts.dimensions]; - var meshData = volumePlot(volumeOpts, bounds); + var meshData = volumePlot(gl, volumeOpts, bounds); // pass gl-mesh3d lighting attributes var lp = trace.lightposition; @@ -183,13 +183,11 @@ proto.dispose = function() { function createvolumeTrace(scene, data) { var gl = scene.glplot.gl; - var meshData = convert(scene, data); - var mesh = volumePlot.createTriMesh(gl, meshData); + var mesh = convert(gl, scene, data); var volume = new Volume(scene, data.uid); volume.mesh = mesh; volume.data = data; - volume.meshData = meshData; mesh._trace = volume; scene.glplot.add(mesh); From d3fc93f427e8db1f3902df370a0536bf2d296a73 Mon Sep 17 00:00:00 2001 From: Ilmari Heikkinen Date: Mon, 25 Jun 2018 08:34:43 +0800 Subject: [PATCH 03/20] volume attributes tweaks, something renders --- src/traces/volume/attributes.js | 32 ++++++++++---------------------- src/traces/volume/convert.js | 32 +++++++++++++++++++------------- src/traces/volume/defaults.js | 7 ++++--- 3 files changed, 33 insertions(+), 38 deletions(-) diff --git a/src/traces/volume/attributes.js b/src/traces/volume/attributes.js index 988483f3f8b..a70398ff1fc 100644 --- a/src/traces/volume/attributes.js +++ b/src/traces/volume/attributes.js @@ -22,7 +22,7 @@ var attrs = { role: 'info', editType: 'calc+clearAxisTypes', description: [ - 'Sets the x coordinates of the isosurface' + 'Sets the x coordinates of the volume' ].join(' ') }, y: { @@ -30,7 +30,7 @@ var attrs = { role: 'info', editType: 'calc+clearAxisTypes', description: [ - 'Sets the y coordinates of the isosurface' + 'Sets the y coordinates of the volume' ].join(' ') }, z: { @@ -38,44 +38,32 @@ var attrs = { role: 'info', editType: 'calc+clearAxisTypes', description: [ - 'Sets the z coordinates of the isosurface' + 'Sets the z coordinates of the volume' ].join(' ') }, u: { valType: 'data_array', editType: 'calc', - description: 'Sets the intensity values of the isosurface.' + description: 'Sets the intensity values of the volume.' }, imin: { valType: 'number', editType: 'calc', - description: 'Sets the minimum iso bound of the isosurface.' + description: 'Sets the minimum intensity bound of the volume.' }, imax: { valType: 'number', editType: 'calc', - description: 'Sets the maximum iso bound of the isosurface.' + description: 'Sets the maximum intensity bound of the volume.' }, - smoothnormals: { - valType: 'boolean', - editType: 'calc', - description: '' - }, - - singlemesh: { - valType: 'boolean', - editType: 'calc', - description: '' - }, - - isocaps: { - valType: 'boolean', + opacity: { + valType: 'number', editType: 'calc', - description: '' + description: 'Sets the opacity of the volume.' }, boundmin: { @@ -97,7 +85,7 @@ var attrs = { arrayOk: true, editType: 'calc', description: [ - 'Sets the text elements associated with the isosurface points.', + 'Sets the text elements associated with the volume points.', 'If trace `hoverinfo` contains a *text* flag and *hovertext* is not set,', 'these elements will be seen in the hover labels.' ].join(' ') diff --git a/src/traces/volume/convert.js b/src/traces/volume/convert.js index 499fe97d4ba..ffa21617523 100644 --- a/src/traces/volume/convert.js +++ b/src/traces/volume/convert.js @@ -46,7 +46,7 @@ cmin: 1500, cmax: 2000, - isocaps: true, + opacity: 1, colorscale: 'Portland' }], { @@ -146,26 +146,30 @@ function convert(gl, scene, trace) { volumeOpts.colormap = parseColorScale(trace.colorscale); - volumeOpts.vertexIntensityBounds = [trace.cmin, trace.cmax]; + volumeOpts.intensityBounds = [trace.cmin, trace.cmax]; volumeOpts.isoBounds = [trace.imin, trace.imax]; - volumeOpts.isoCaps = trace.isocaps; + volumeOpts.opacity = trace.opacity === undefined ? 1 : trace.opacity; var bounds = [[0, 0, 0], volumeOpts.dimensions]; - var meshData = volumePlot(gl, volumeOpts, bounds); + var volume = volumePlot(gl, volumeOpts, bounds); // pass gl-mesh3d lighting attributes var lp = trace.lightposition; - meshData.lightPosition = [lp.x, lp.y, lp.z]; - meshData.ambient = trace.lighting.ambient; - meshData.diffuse = trace.lighting.diffuse; - meshData.specular = trace.lighting.specular; - meshData.roughness = trace.lighting.roughness; - meshData.fresnel = trace.lighting.fresnel; - meshData.opacity = trace.opacity; - - return meshData; + for(var i = 0; i < volume.meshes.length; i++) { + var meshData = volume.meshes[i]; + meshData.lightPosition = [lp.x, lp.y, lp.z]; + meshData.ambient = trace.lighting.ambient; + meshData.diffuse = trace.lighting.diffuse; + meshData.specular = trace.lighting.specular; + meshData.roughness = trace.lighting.roughness; + meshData.fresnel = trace.lighting.fresnel; + meshData.opacity = trace.opacity; + + } + + return volume; } proto.update = function(data) { @@ -189,6 +193,8 @@ function createvolumeTrace(scene, data) { volume.mesh = mesh; volume.data = data; mesh._trace = volume; + mesh.isOpaque = function() { return true; }; + mesh.isTransparent = function() { return false; }; scene.glplot.add(mesh); diff --git a/src/traces/volume/defaults.js b/src/traces/volume/defaults.js index 8396a64c2f8..4afc98f346d 100644 --- a/src/traces/volume/defaults.js +++ b/src/traces/volume/defaults.js @@ -35,9 +35,10 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout coerce('imin'); coerce('imax'); - coerce('smoothnormals'); - coerce('isocaps'); - // coerce('singlemesh'); + coerce('opacity'); + + coerce('boundmin'); + coerce('boundmax'); coerce('lighting.ambient'); coerce('lighting.diffuse'); From 9e8378d95127c8bb6d3bf06fedfb47adbe8bfa60 Mon Sep 17 00:00:00 2001 From: Ilmari Heikkinen Date: Mon, 25 Jun 2018 14:06:31 +0800 Subject: [PATCH 04/20] volume trace rendering sort of works, automatic default isoBounds --- src/traces/volume/convert.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/traces/volume/convert.js b/src/traces/volume/convert.js index ffa21617523..90da068c174 100644 --- a/src/traces/volume/convert.js +++ b/src/traces/volume/convert.js @@ -46,7 +46,7 @@ cmin: 1500, cmax: 2000, - opacity: 1, + opacity: 0.5, colorscale: 'Portland' }], { @@ -147,7 +147,10 @@ function convert(gl, scene, trace) { volumeOpts.colormap = parseColorScale(trace.colorscale); volumeOpts.intensityBounds = [trace.cmin, trace.cmax]; - volumeOpts.isoBounds = [trace.imin, trace.imax]; + volumeOpts.isoBounds = [ + trace.imin === undefined ? trace.cmin : trace.imin, + trace.imax === undefined ? trace.cmax : trace.imax + ]; volumeOpts.opacity = trace.opacity === undefined ? 1 : trace.opacity; @@ -193,8 +196,6 @@ function createvolumeTrace(scene, data) { volume.mesh = mesh; volume.data = data; mesh._trace = volume; - mesh.isOpaque = function() { return true; }; - mesh.isTransparent = function() { return false; }; scene.glplot.add(mesh); From 0691672d71a1444c9546b756a093d3a27a6181dd Mon Sep 17 00:00:00 2001 From: Ilmari Heikkinen Date: Mon, 25 Jun 2018 14:53:59 +0800 Subject: [PATCH 05/20] volume: add opacityscale param --- src/traces/volume/attributes.js | 7 +++++++ src/traces/volume/convert.js | 19 ++++++++++++------- src/traces/volume/defaults.js | 1 + 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/traces/volume/attributes.js b/src/traces/volume/attributes.js index a70398ff1fc..e20bd442acc 100644 --- a/src/traces/volume/attributes.js +++ b/src/traces/volume/attributes.js @@ -44,10 +44,17 @@ var attrs = { u: { valType: 'data_array', + role: 'info', editType: 'calc', description: 'Sets the intensity values of the volume.' }, + opacityscale: { + valType: 'data_array', + editType: 'calc', + description: 'Sets the opacity scale of the volume, which opacity to use for which intensity. Array of 256 values in 0..1 range.' + }, + imin: { valType: 'number', editType: 'calc', diff --git a/src/traces/volume/convert.js b/src/traces/volume/convert.js index 90da068c174..ce540b926a3 100644 --- a/src/traces/volume/convert.js +++ b/src/traces/volume/convert.js @@ -41,14 +41,15 @@ u: data, - imin: 1600, - imax: 2000, + imin: 0, + imax: 3000, cmin: 1500, - cmax: 2000, + cmax: 4000, - opacity: 0.5, + opacity: 0.1, - colorscale: 'Portland' + colorscale: 'Portland', + opacityscale: alphascale }], { scene: { xaxis: {range: [0, 1]}, @@ -146,8 +147,12 @@ function convert(gl, scene, trace) { volumeOpts.colormap = parseColorScale(trace.colorscale); - volumeOpts.intensityBounds = [trace.cmin, trace.cmax]; - volumeOpts.isoBounds = [ + if(trace.opacityscale) { + volumeOpts.alphamap = trace.opacityscale; + } + + volumeOpts.isoBounds = [trace.cmin, trace.cmax]; + volumeOpts.intensityBounds = [ trace.imin === undefined ? trace.cmin : trace.imin, trace.imax === undefined ? trace.cmax : trace.imax ]; diff --git a/src/traces/volume/defaults.js b/src/traces/volume/defaults.js index 4afc98f346d..4c67e5b7b6b 100644 --- a/src/traces/volume/defaults.js +++ b/src/traces/volume/defaults.js @@ -36,6 +36,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout coerce('imin'); coerce('imax'); coerce('opacity'); + coerce('opacityscale'); coerce('boundmin'); coerce('boundmax'); From a48ee0e4d9bd809f59631d1635601b24865cd35d Mon Sep 17 00:00:00 2001 From: Ilmari Heikkinen Date: Mon, 25 Jun 2018 17:26:02 +0800 Subject: [PATCH 06/20] fix volume update, cooler example function --- src/traces/volume/convert.js | 46 ++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/src/traces/volume/convert.js b/src/traces/volume/convert.js index ce540b926a3..5d7efc8c6e1 100644 --- a/src/traces/volume/convert.js +++ b/src/traces/volume/convert.js @@ -9,29 +9,34 @@ /* Usage example: - var width = 64 - var height = 64 - var depth = 64 + var width = 128 + var height = 128 + var depth = 128 var xs = [] var ys = [] var zs = [] - var data = new Uint16Array(width*height*depth) + var data = [] for (var z=0; z Date: Wed, 11 Jul 2018 10:35:40 -0400 Subject: [PATCH 07/20] add gl-volume3d (w/ updated deps) to package-lock --- package-lock.json | 19 +++++++++++++++++++ package.json | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index ed8e620daca..29d65b4546a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5229,6 +5229,25 @@ "resolved": "https://registry.npmjs.org/gl-vec4/-/gl-vec4-1.0.1.tgz", "integrity": "sha1-l9loeCgbFLUyy84QF4Xf0cs0CWQ=" }, + "gl-volume3d": { + "version": "git+https://github.com/gl-vis/gl-volume3d.git#c222d11c950230e1882b3072673924474090f856", + "requires": { + "barycentric": "1.0.1", + "colormap": "2.3.0", + "gl-buffer": "2.1.2", + "gl-mat4": "1.2.0", + "gl-shader": "4.2.1", + "gl-texture2d": "2.1.0", + "gl-vao": "1.3.0", + "glsl-specular-cook-torrance": "2.0.1", + "glslify": "6.1.1", + "ndarray": "1.0.18", + "normals": "1.1.0", + "polytope-closest-point": "1.0.0", + "simplicial-complex-contour": "1.0.2", + "typedarray-pool": "1.1.0" + } + }, "glob": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", diff --git a/package.json b/package.json index d38108111f0..d309ad70697 100644 --- a/package.json +++ b/package.json @@ -86,7 +86,7 @@ "gl-surface3d": "^1.3.5", "gl-streamtube3d": "^1.0.0", "@etpinard/gl-text": "^1.1.6", - "gl-volume3d": "https://github.com/gl-vis/gl-volume3d.git", + "gl-volume3d": "git+https://github.com/gl-vis/gl-volume3d.git", "glslify": "^6.1.1", "has-hover": "^1.0.1", "has-passive-events": "^1.0.0", From bbaaebbcb140fec3ae499c7a63bdd75ec2f1e474 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Wed, 11 Jul 2018 10:43:35 -0400 Subject: [PATCH 08/20] use 'value' instead of 'u' --- src/traces/volume/attributes.js | 11 ++++++----- src/traces/volume/calc.js | 21 +++++++++------------ src/traces/volume/convert.js | 4 ++-- src/traces/volume/defaults.js | 4 ++-- 4 files changed, 19 insertions(+), 21 deletions(-) diff --git a/src/traces/volume/attributes.js b/src/traces/volume/attributes.js index e20bd442acc..750fe060078 100644 --- a/src/traces/volume/attributes.js +++ b/src/traces/volume/attributes.js @@ -8,7 +8,6 @@ 'use strict'; -var colorAttrs = require('../../components/colorscale/color_attributes'); var colorscaleAttrs = require('../../components/colorscale/attributes'); var colorbarAttrs = require('../../components/colorbar/attributes'); var mesh3dAttrs = require('../mesh3d/attributes'); @@ -42,7 +41,7 @@ var attrs = { ].join(' ') }, - u: { + value: { valType: 'data_array', role: 'info', editType: 'calc', @@ -99,11 +98,13 @@ var attrs = { } }; -extendFlat(attrs, colorAttrs('', 'calc', true), { - showscale: colorscaleAttrs.showscale, +extendFlat(attrs, colorscaleAttrs('', { + colorAttr: 'value', + showScaleDflt: true, + editTypeOverride: 'calc' +}), { colorbar: colorbarAttrs }); -delete attrs.color; var fromMesh3d = ['opacity', 'lightposition', 'lighting']; diff --git a/src/traces/volume/calc.js b/src/traces/volume/calc.js index 4d90bbed482..9ad6b33ae07 100644 --- a/src/traces/volume/calc.js +++ b/src/traces/volume/calc.js @@ -11,20 +11,17 @@ var colorscaleCalc = require('../../components/colorscale/calc'); module.exports = function calc(gd, trace) { - var u = trace.u; - var len = u.length; - var normMax = -Infinity; - var normMin = Infinity; + var value = trace.value; + var len = value.length; + var vMax = -Infinity; + var vMin = Infinity; for(var i = 0; i < len; i++) { - var uu = u[i]; - var norm = uu; - - normMax = Math.max(normMax, norm); - normMin = Math.min(normMin, norm); + var v = value[i]; + vMax = Math.max(vMax, v); + vMin = Math.min(vMin, v); } - trace._normMax = normMax; - - colorscaleCalc(trace, [normMin, normMax], '', 'c'); + trace._vMax = vMax; + colorscaleCalc(trace, [vMin, vMax], '', 'c'); }; diff --git a/src/traces/volume/convert.js b/src/traces/volume/convert.js index 5d7efc8c6e1..dafb2bd0d88 100644 --- a/src/traces/volume/convert.js +++ b/src/traces/volume/convert.js @@ -44,7 +44,7 @@ y: ys, z: zs, - u: data, + value: data, cmin: 0.05, cmax: 0.22, @@ -147,7 +147,7 @@ function convert(gl, scene, trace) { // ]; - volumeOpts.values = trace.u; + volumeOpts.values = trace.value; volumeOpts.colormap = parseColorScale(trace.colorscale); diff --git a/src/traces/volume/defaults.js b/src/traces/volume/defaults.js index 4c67e5b7b6b..d6ac5fd0041 100644 --- a/src/traces/volume/defaults.js +++ b/src/traces/volume/defaults.js @@ -19,14 +19,14 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout return Lib.coerce(traceIn, traceOut, attributes, attr, dflt); } - var u = coerce('u'); + var value = coerce('value'); var x = coerce('x'); var y = coerce('y'); var z = coerce('z'); if( - !u || !u.length || + !value || !value.length || !x || !x.length || !y || !y.length || !z || !z.length ) { traceOut.visible = false; From c392a7dacec4410ee83207cd690145d72ce8fc2f Mon Sep 17 00:00:00 2001 From: Ilmari Heikkinen Date: Mon, 27 Aug 2018 16:47:30 +0800 Subject: [PATCH 09/20] add gl-volume3d npm package --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d309ad70697..1f229812346 100644 --- a/package.json +++ b/package.json @@ -86,7 +86,7 @@ "gl-surface3d": "^1.3.5", "gl-streamtube3d": "^1.0.0", "@etpinard/gl-text": "^1.1.6", - "gl-volume3d": "git+https://github.com/gl-vis/gl-volume3d.git", + "gl-volume3d": "^0.5.0", "glslify": "^6.1.1", "has-hover": "^1.0.1", "has-passive-events": "^1.0.0", From d7ae2fd1a78b49ed584e433eb43d72d47c3e271d Mon Sep 17 00:00:00 2001 From: Ilmari Heikkinen Date: Mon, 27 Aug 2018 16:54:56 +0800 Subject: [PATCH 10/20] add volume to lib/index-gl3d.js --- lib/index-gl3d.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/index-gl3d.js b/lib/index-gl3d.js index 8a64c3fc8d6..2a2abd01c64 100644 --- a/lib/index-gl3d.js +++ b/lib/index-gl3d.js @@ -15,7 +15,8 @@ Plotly.register([ require('./surface'), require('./mesh3d'), require('./cone'), - require('./streamtube') + require('./streamtube'), + require('./volume') ]); module.exports = Plotly; From ce212cbd06b2081cc08ab1a75c1724a0f0cfd4f2 Mon Sep 17 00:00:00 2001 From: Ilmari Heikkinen Date: Mon, 27 Aug 2018 16:55:20 +0800 Subject: [PATCH 11/20] createvolumeTrace -> createVolumeTrace --- src/traces/volume/convert.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/traces/volume/convert.js b/src/traces/volume/convert.js index dafb2bd0d88..17cda70631f 100644 --- a/src/traces/volume/convert.js +++ b/src/traces/volume/convert.js @@ -202,7 +202,7 @@ proto.dispose = function() { this.mesh.dispose(); }; -function createvolumeTrace(scene, data) { +function createVolumeTrace(scene, data) { var gl = scene.glplot.gl; var mesh = convert(gl, scene, data); @@ -217,4 +217,4 @@ function createvolumeTrace(scene, data) { return volume; } -module.exports = createvolumeTrace; +module.exports = createVolumeTrace; From 6866c183486e9356742e9ed04dd80bd9d68d13a9 Mon Sep 17 00:00:00 2001 From: Ilmari Heikkinen Date: Mon, 10 Sep 2018 11:21:38 +0800 Subject: [PATCH 12/20] volume trace hover text scaffolding --- src/traces/volume/convert.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/traces/volume/convert.js b/src/traces/volume/convert.js index 17cda70631f..bf34e993ce6 100644 --- a/src/traces/volume/convert.js +++ b/src/traces/volume/convert.js @@ -21,8 +21,8 @@ for (var z=0; z Date: Mon, 10 Sep 2018 11:24:26 +0800 Subject: [PATCH 13/20] add roles to volume attributes --- src/traces/volume/attributes.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/traces/volume/attributes.js b/src/traces/volume/attributes.js index 750fe060078..5bee99af2d1 100644 --- a/src/traces/volume/attributes.js +++ b/src/traces/volume/attributes.js @@ -50,36 +50,42 @@ var attrs = { opacityscale: { valType: 'data_array', + role: 'info', editType: 'calc', description: 'Sets the opacity scale of the volume, which opacity to use for which intensity. Array of 256 values in 0..1 range.' }, imin: { valType: 'number', + role: 'info', editType: 'calc', description: 'Sets the minimum intensity bound of the volume.' }, imax: { valType: 'number', + role: 'info', editType: 'calc', description: 'Sets the maximum intensity bound of the volume.' }, opacity: { valType: 'number', + role: 'info', editType: 'calc', description: 'Sets the opacity of the volume.' }, boundmin: { valType: 'data_array', + role: 'info', editType: 'calc', description: '' }, boundmax: { valType: 'data_array', + role: 'info', editType: 'calc', description: '' }, From 34a1aa7d6cef10b2c4acef70061c5f7d7506f412 Mon Sep 17 00:00:00 2001 From: Ilmari Heikkinen Date: Thu, 13 Sep 2018 17:09:57 +0800 Subject: [PATCH 14/20] volume lint fixes --- src/traces/volume/convert.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/traces/volume/convert.js b/src/traces/volume/convert.js index bf34e993ce6..a0124b4ae48 100644 --- a/src/traces/volume/convert.js +++ b/src/traces/volume/convert.js @@ -89,12 +89,12 @@ proto.handlePick = function(selection) { ]; var text = this.data.text; - selection.textLabel = "value: " + selection.data.intensity.toPrecision(3); + selection.textLabel = 'value: ' + selection.data.intensity.toPrecision(3); if(Array.isArray(text) && text[selectIndex] !== undefined) { - selection.textLabel = "
" + text[selectIndex]; + selection.textLabel = '
' + text[selectIndex]; } else if(text) { - selection.textLabel = "
" + text; + selection.textLabel = '
' + text; } return true; From 4f11e357947fe7384a05c8aa301c686b40c0c3de Mon Sep 17 00:00:00 2001 From: Ilmari Heikkinen Date: Thu, 13 Sep 2018 18:00:31 +0800 Subject: [PATCH 15/20] volume: remove boundmin/max, use default opacity, describe opacitymap and make it colorscale-like --- src/traces/volume/attributes.js | 28 +++++----------------- src/traces/volume/convert.js | 41 ++++++++++++++++++++++++--------- src/traces/volume/defaults.js | 4 ---- 3 files changed, 36 insertions(+), 37 deletions(-) diff --git a/src/traces/volume/attributes.js b/src/traces/volume/attributes.js index 5bee99af2d1..03c51e68397 100644 --- a/src/traces/volume/attributes.js +++ b/src/traces/volume/attributes.js @@ -52,7 +52,12 @@ var attrs = { valType: 'data_array', role: 'info', editType: 'calc', - description: 'Sets the opacity scale of the volume, which opacity to use for which intensity. Array of 256 values in 0..1 range.' + description: [ + 'Sets the opacity scale of the volume.', + 'Defines which opacity to use for which intensity.', + 'Multiplied with trace.opacity to obtain the final opacity.', + 'Colorscale-like array of [[0, opacity0], [v1, opacity1], ..., [1, opacityN]].' + ].join(' ') }, imin: { @@ -69,27 +74,6 @@ var attrs = { description: 'Sets the maximum intensity bound of the volume.' }, - opacity: { - valType: 'number', - role: 'info', - editType: 'calc', - description: 'Sets the opacity of the volume.' - }, - - boundmin: { - valType: 'data_array', - role: 'info', - editType: 'calc', - description: '' - }, - - boundmax: { - valType: 'data_array', - role: 'info', - editType: 'calc', - description: '' - }, - text: { valType: 'string', role: 'info', diff --git a/src/traces/volume/convert.js b/src/traces/volume/convert.js index a0124b4ae48..cefbbb1ed53 100644 --- a/src/traces/volume/convert.js +++ b/src/traces/volume/convert.js @@ -33,8 +33,8 @@ } var opacityscale = [] - for (var i=0; i<256; i++) { - opacityscale[i] = Math.pow(i/256, 1.2) + for (var i=0; i<16; i++) { + opacityscale[i] = [i/15, Math.pow(i/15, 1.2)] } Plotly.newPlot(gd, [{ @@ -101,6 +101,33 @@ proto.handlePick = function(selection) { } }; +function parseOpacityScale(opacityScale) { + var alphaMapLength = 256; + var alphaMap = new Float32Array(alphaMapLength); + var alphaMapIndex = 0; + var previousEntry = [0, 1]; + for(var i = 0; i < opacityScale.length; i++) { + var entry = opacityScale[i]; + var startIndex = alphaMapIndex; + var startValue = previousEntry[1]; + var endIndex = Math.max(0, Math.min(Math.floor(entry[0] * alphaMapLength), alphaMapLength-1)); + var endValue = entry[1]; + var indexDelta = endIndex - startIndex; + while(alphaMapIndex < endIndex) { + var t = (alphaMapIndex - startIndex) / indexDelta; + alphaMap[alphaMapIndex] = (1 - t) * startValue + t * endValue; + alphaMapIndex++; + } + alphaMap[alphaMapIndex] = endValue; + previousEntry = entry; + } + var lastAlpha = alphaMap[alphaMapIndex]; + while(alphaMapIndex < alphaMapLength) { + alphaMap[alphaMapIndex++] = lastAlpha; + } + return alphaMap; +} + var axisName2scaleIndex = {xaxis: 0, yaxis: 1, zaxis: 2}; function getSequence(src) { @@ -141,18 +168,12 @@ function convert(gl, scene, trace) { toDataCoords(zs, 'zaxis') ]; - // var bounds = [ - // volumeOpts.boundmin || [xs[0], ys[0], zs[0]], - // volumeOpts.boundmax || [xs[xs.length - 1], ys[ys.length - 1], zs[zs.length - 1]] - // ]; - - volumeOpts.values = trace.value; volumeOpts.colormap = parseColorScale(trace.colorscale); if(trace.opacityscale) { - volumeOpts.alphamap = trace.opacityscale; + volumeOpts.alphamap = parseOpacityScale(trace.opacityscale); } volumeOpts.isoBounds = [trace.cmin, trace.cmax]; @@ -161,8 +182,6 @@ function convert(gl, scene, trace) { trace.imax === undefined ? trace.cmax : trace.imax ]; - volumeOpts.opacity = trace.opacity === undefined ? 1 : trace.opacity; - var bounds = [[0, 0, 0], volumeOpts.dimensions]; var volume = volumePlot(gl, volumeOpts, bounds); diff --git a/src/traces/volume/defaults.js b/src/traces/volume/defaults.js index d6ac5fd0041..8996c56cc63 100644 --- a/src/traces/volume/defaults.js +++ b/src/traces/volume/defaults.js @@ -35,12 +35,8 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout coerce('imin'); coerce('imax'); - coerce('opacity'); coerce('opacityscale'); - coerce('boundmin'); - coerce('boundmax'); - coerce('lighting.ambient'); coerce('lighting.diffuse'); coerce('lighting.specular'); From 0b064237a2349cbea04c8347a2dafd8c4a23485f Mon Sep 17 00:00:00 2001 From: Ilmari Heikkinen Date: Thu, 20 Sep 2018 14:16:06 +0800 Subject: [PATCH 16/20] fix lint issue --- src/traces/volume/convert.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/traces/volume/convert.js b/src/traces/volume/convert.js index cefbbb1ed53..e581f058ddb 100644 --- a/src/traces/volume/convert.js +++ b/src/traces/volume/convert.js @@ -110,7 +110,7 @@ function parseOpacityScale(opacityScale) { var entry = opacityScale[i]; var startIndex = alphaMapIndex; var startValue = previousEntry[1]; - var endIndex = Math.max(0, Math.min(Math.floor(entry[0] * alphaMapLength), alphaMapLength-1)); + var endIndex = Math.max(0, Math.min(Math.floor(entry[0] * alphaMapLength), alphaMapLength - 1)); var endValue = entry[1]; var indexDelta = endIndex - startIndex; while(alphaMapIndex < endIndex) { From 34b40f09b9a1c12c0ac51f12a123b64d72fa0d31 Mon Sep 17 00:00:00 2001 From: Ilmari Heikkinen Date: Thu, 20 Sep 2018 14:18:39 +0800 Subject: [PATCH 17/20] volume/convert.js: bring back trace.opacity --- src/traces/volume/convert.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/traces/volume/convert.js b/src/traces/volume/convert.js index e581f058ddb..18c6702c016 100644 --- a/src/traces/volume/convert.js +++ b/src/traces/volume/convert.js @@ -172,6 +172,8 @@ function convert(gl, scene, trace) { volumeOpts.colormap = parseColorScale(trace.colorscale); + volumeOpts.opacity = trace.opacity; + if(trace.opacityscale) { volumeOpts.alphamap = parseOpacityScale(trace.opacityscale); } From 4c6f59cd029b75bbb63cd002e323c01d5325d0a8 Mon Sep 17 00:00:00 2001 From: Ilmari Heikkinen Date: Fri, 28 Sep 2018 15:43:48 +0800 Subject: [PATCH 18/20] volume: document vmin, vmax, cmin, cmax, rename value to values --- src/traces/volume/attributes.js | 30 +++++++---- src/traces/volume/calc.js | 6 +-- src/traces/volume/convert.js | 90 ++++++++++++++++++--------------- src/traces/volume/defaults.js | 10 ++-- 4 files changed, 78 insertions(+), 58 deletions(-) diff --git a/src/traces/volume/attributes.js b/src/traces/volume/attributes.js index 03c51e68397..02fa2e7cadd 100644 --- a/src/traces/volume/attributes.js +++ b/src/traces/volume/attributes.js @@ -41,7 +41,7 @@ var attrs = { ].join(' ') }, - value: { + values: { valType: 'data_array', role: 'info', editType: 'calc', @@ -60,18 +60,32 @@ var attrs = { ].join(' ') }, - imin: { + vmin: { valType: 'number', role: 'info', editType: 'calc', - description: 'Sets the minimum intensity bound of the volume.' + description: 'Sets the minimum intensity bound of the volume. Defaults to the smallest value in values.' }, - imax: { + vmax: { valType: 'number', role: 'info', editType: 'calc', - description: 'Sets the maximum intensity bound of the volume.' + description: 'Sets the maximum intensity bound of the volume. Defaults to the largest value in values.' + }, + + cmin: { + valType: 'number', + role: 'info', + editType: 'calc', + description: 'Sets the colorscale start intensity of the volume. Defaults to the smallest value in values.' + }, + + cmax: { + valType: 'number', + role: 'info', + editType: 'calc', + description: 'Sets the colorscale end intensity of the volume. Defaults to the largest value in values.' }, text: { @@ -102,10 +116,4 @@ fromMesh3d.forEach(function(k) { attrs[k] = mesh3dAttrs[k]; }); -attrs.hoverinfo = extendFlat({}, baseAttrs.hoverinfo, { - editType: 'calc', - flags: ['x', 'y', 'z', 'intensity', 'text', 'name'], - dflt: 'x+y+z+intensity+text+name' -}); - module.exports = attrs; diff --git a/src/traces/volume/calc.js b/src/traces/volume/calc.js index 9ad6b33ae07..8753690ab94 100644 --- a/src/traces/volume/calc.js +++ b/src/traces/volume/calc.js @@ -11,13 +11,13 @@ var colorscaleCalc = require('../../components/colorscale/calc'); module.exports = function calc(gd, trace) { - var value = trace.value; - var len = value.length; + var values = trace.values; + var len = values.length; var vMax = -Infinity; var vMin = Infinity; for(var i = 0; i < len; i++) { - var v = value[i]; + var v = values[i]; vMax = Math.max(vMax, v); vMin = Math.min(vMin, v); } diff --git a/src/traces/volume/convert.js b/src/traces/volume/convert.js index 18c6702c016..ac8f698ffd8 100644 --- a/src/traces/volume/convert.js +++ b/src/traces/volume/convert.js @@ -44,13 +44,13 @@ y: ys, z: zs, - value: data, + values: data, - cmin: 0.05, - cmax: 0.22, + vmin: 0.05, + vmax: 0.22, - imin: 0.05, - imax: 0.25, + cmin: 0.05, + cmax: 0.25, opacity: 0.05, @@ -65,6 +65,8 @@ var volumePlot = require('gl-volume3d'); var simpleMap = require('../../lib').simpleMap; var parseColorScale = require('../../lib/gl_format_color').parseColorScale; +var distinctVals = require('../../lib').distinctVals; + function Volume(scene, uid) { this.scene = scene; this.uid = uid; @@ -75,30 +77,7 @@ function Volume(scene, uid) { var proto = Volume.prototype; proto.handlePick = function(selection) { - // TODO - // Raymarch into the object volume to find a voxel with intensity higher than a user-definable limit. - // - if(selection.object === this.mesh) { - var selectIndex = selection.index = selection.data.index; - - selection.traceCoordinate = [ - selection.data.position[0], - selection.data.position[1], - selection.data.position[2], - selection.data.intensity - ]; - - var text = this.data.text; - selection.textLabel = 'value: ' + selection.data.intensity.toPrecision(3); - - if(Array.isArray(text) && text[selectIndex] !== undefined) { - selection.textLabel = '
' + text[selectIndex]; - } else if(text) { - selection.textLabel = '
' + text; - } - - return true; - } + return false; }; function parseOpacityScale(opacityScale) { @@ -130,9 +109,22 @@ function parseOpacityScale(opacityScale) { var axisName2scaleIndex = {xaxis: 0, yaxis: 1, zaxis: 2}; -function getSequence(src) { +/* + Finds the first ascending sequence of unique coordinates in src. + Steps through src in stride-length steps. + + Useful for creating meshgrids out of 3D volume coordinates. + + E.g. + getSequence([1,2,3,1,2,3], 1) -> [1,2,3] // steps through the first half of the array, bails on the second 1 + getSequence([1,1,2,2,3,3,1,1,2,2,3,3], 1) -> [1,2,3] // steps through every element in the first half of the array + getSequence([1,1,2,2,3,3,1,1,2,2,3,3], 2) -> [1,2,3] // skips every other element + + getSequence([1,1,1, 1,1,1, 1,1,1, 2,2,2, 2,2,2, 2,2,2], 9) -> [1,2] // skips from seq[0] to seq[9] to end of array +*/ +function getSequence(src, stride) { var xs = [src[0]]; - for(var i = 1, last = xs[0]; i < src.length; i++) { + for(var i = 0, last = xs[0]; i < src.length; i += stride) { var p = src[i]; if(p >= last) { if(p > last) { @@ -157,9 +149,9 @@ function convert(gl, scene, trace) { return simpleMap(arr, function(v) { return ax.d2l(v) * scale; }); } - var xs = getSequence(trace.x); - var ys = getSequence(trace.y); - var zs = getSequence(trace.z); + var xs = getSequence(trace.x, 1); + var ys = getSequence(trace.y, xs.length); + var zs = getSequence(trace.z, xs.length * ys.length); volumeOpts.dimensions = [xs.length, ys.length, zs.length]; volumeOpts.meshgrid = [ @@ -168,7 +160,7 @@ function convert(gl, scene, trace) { toDataCoords(zs, 'zaxis') ]; - volumeOpts.values = trace.value; + volumeOpts.values = trace.values; volumeOpts.colormap = parseColorScale(trace.colorscale); @@ -178,11 +170,29 @@ function convert(gl, scene, trace) { volumeOpts.alphamap = parseOpacityScale(trace.opacityscale); } - volumeOpts.isoBounds = [trace.cmin, trace.cmax]; - volumeOpts.intensityBounds = [ - trace.imin === undefined ? trace.cmin : trace.imin, - trace.imax === undefined ? trace.cmax : trace.imax - ]; + var vmin = trace.vmin; + var vmax = trace.vmax; + + if(vmin === undefined || vmax === undefined) { + var minV = trace.values[0], maxV = trace.values[0]; + for(var i = 1; i < trace.values.length; i++) { + var v = trace.values[v]; + if(v > maxV) { + maxV = v; + } else if(v < minV) { + minV = v; + } + } + if(vmin === undefined) { + vmin = minV; + } + if(vmax === undefined) { + vmax = maxV; + } + } + + volumeOpts.isoBounds = [vmin, vmax]; + volumeOpts.intensityBounds = [trace.cmin, trace.cmax]; var bounds = [[0, 0, 0], volumeOpts.dimensions]; diff --git a/src/traces/volume/defaults.js b/src/traces/volume/defaults.js index 8996c56cc63..bd6350933f9 100644 --- a/src/traces/volume/defaults.js +++ b/src/traces/volume/defaults.js @@ -19,22 +19,24 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout return Lib.coerce(traceIn, traceOut, attributes, attr, dflt); } - var value = coerce('value'); + var values = coerce('values'); var x = coerce('x'); var y = coerce('y'); var z = coerce('z'); if( - !value || !value.length || + !values || !values.length || !x || !x.length || !y || !y.length || !z || !z.length ) { traceOut.visible = false; return; } - coerce('imin'); - coerce('imax'); + coerce('vmin'); + coerce('vmax'); + coerce('cmin'); + coerce('cmax'); coerce('opacityscale'); coerce('lighting.ambient'); From d78825097e09127a0c8785c24217d87ccd41b934 Mon Sep 17 00:00:00 2001 From: Ilmari Heikkinen Date: Wed, 3 Oct 2018 20:44:17 +0800 Subject: [PATCH 19/20] volume plot tests, initial scaffold --- test/image/mocks/gl3d_volume-simple.json | 66 ++++++++++++ test/jasmine/tests/volume_test.js | 126 +++++++++++++++++++++++ 2 files changed, 192 insertions(+) create mode 100644 test/image/mocks/gl3d_volume-simple.json create mode 100644 test/jasmine/tests/volume_test.js diff --git a/test/image/mocks/gl3d_volume-simple.json b/test/image/mocks/gl3d_volume-simple.json new file mode 100644 index 00000000000..a27ad28fc27 --- /dev/null +++ b/test/image/mocks/gl3d_volume-simple.json @@ -0,0 +1,66 @@ +{ + "data": [ + { + "type": "volume", + "x": [1, 2, 3], + "y": [1, 2, 3], + "z": [1, 2, 3], + "values": [ + 0,1,2, + 3,4,5, + 6,7,8, + + 0,1,2, + 3,4,5, + 6,7,8, + + 0,1,2, + 3,4,5, + 6,7,8 + ], + "sizemode": "absolute", + "sizeref": 2, + "anchor": "tip", + "colorbar": { + "x": 0, + "xanchor": "right", + "side": "left" + } + }, + { + "type": "volume", + "x": [1, 2, 3], + "y": [1, 2, 3], + "z": [1, 2, 3], + "values": [ + 0,1,2, + 3,4,5, + 6,7,8, + + 0,1,2, + 3,4,5, + 6,7,8, + + 0,1,2, + 3,4,5, + 6,7,8 + ], + "scene": "scene2" + } + ], + "layout": { + "scene": { + "domain": {"x": [0, 0.5]}, + "camera": { + "eye": {"x": -1.57, "y": 1.36, "z": 0.58} + } + }, + "scene2": { + "domain": {"x": [0.5, 1]}, + "camera": { + "eye": {"x": -1.57, "y": 1.36, "z": 0.58} + } + }, + "width": 800 + } +} diff --git a/test/jasmine/tests/volume_test.js b/test/jasmine/tests/volume_test.js new file mode 100644 index 00000000000..657a8b78628 --- /dev/null +++ b/test/jasmine/tests/volume_test.js @@ -0,0 +1,126 @@ +var Plotly = require('@lib'); +var Lib = require('@src/lib'); + +var supplyAllDefaults = require('../assets/supply_defaults'); +var createGraphDiv = require('../assets/create_graph_div'); +var destroyGraphDiv = require('../assets/destroy_graph_div'); +var failTest = require('../assets/fail_test'); +var delay = require('../assets/delay'); +var mouseEvent = require('../assets/mouse_event'); + +var customAssertions = require('../assets/custom_assertions'); +var assertHoverLabelContent = customAssertions.assertHoverLabelContent; + +describe('Test volume defaults', function() { + var gd; + + function makeGD() { + return { + data: [{ + type: 'volume', + x: [1, 2], + y: [1, 2], + z: [1, 2], + values: [1, 2, 1, 2, 1, 2, 1, 2], + }], + layout: {} + }; + } + + it('should not set `visible: false` for traces with x,y,z,values arrays', function() { + gd = makeGD(); + supplyAllDefaults(gd); + expect(gd._fullData[0].visible).toBe(true); + }); + + it('should set `visible: false` for traces missing x,y,z,values arrays', function() { + var keysToDelete = ['x', 'y', 'z', 'values']; + + keysToDelete.forEach(function(k) { + gd = makeGD(); + delete gd.data[0][k]; + + supplyAllDefaults(gd); + expect(gd._fullData[0].visible).toBe(!k, 'missing array ' + k); + }); + }); + + it('should set `visible: false` for traces empty x,y,z,values arrays', function() { + var keysToEmpty = ['x', 'y', 'z', 'values']; + + keysToEmpty.forEach(function(k) { + gd = makeGD(); + gd.data[0][k] = []; + + supplyAllDefaults(gd); + expect(gd._fullData[0].visible).toBe(!k, 'empty array ' + k); + }); + }); +}); + +describe('Test volume autorange:', function() { + var gd; + + beforeEach(function() { + gd = createGraphDiv(); + }); + + afterEach(function() { + Plotly.purge(gd); + destroyGraphDiv(); + }); + + function _assertAxisRanges(msg, xrng, yrng, zrng) { + var sceneLayout = gd._fullLayout.scene; + expect(sceneLayout.xaxis.range).toBeCloseToArray(xrng, 2, 'xaxis range - ' + msg); + expect(sceneLayout.yaxis.range).toBeCloseToArray(yrng, 2, 'yaxis range - ' + msg); + expect(sceneLayout.zaxis.range).toBeCloseToArray(zrng, 2, 'zaxis range - ' + msg); + } + + +}); + +describe('Test volume interactions', function() { + var gd; + + beforeEach(function() { + gd = createGraphDiv(); + }); + + afterEach(function() { + Plotly.purge(gd); + destroyGraphDiv(); + }); + + it('@gl should add/clear gl objects correctly', function(done) { + var fig = Lib.extendDeep({}, require('@mocks/gl3d_volume-simple.json')); + // put traces on same subplot + delete fig.data[1].scene; + + Plotly.plot(gd, fig).then(function() { + var scene = gd._fullLayout.scene._scene; + var objs = scene.glplot.objects; + + expect(objs.length).toBe(2); + + return Plotly.deleteTraces(gd, [0]); + }) + .then(function() { + var scene = gd._fullLayout.scene._scene; + var objs = scene.glplot.objects; + + expect(objs.length).toBe(1); + + return Plotly.deleteTraces(gd, [0]); + }) + .then(function() { + var scene = gd._fullLayout.scene; + + expect(scene).toBeUndefined(); + }) + .catch(failTest) + .then(done); + }); + + +}); From 8f93b025ce9056d9272d04e1fa4658c3a528a4ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Mon, 29 Oct 2018 18:19:06 -0400 Subject: [PATCH 20/20] bump gl-volume3d to kig's latest --- package-lock.json | 33 +++++++++++++++++---------------- package.json | 2 +- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4e92ab8dd2a..d1e799f944f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4941,22 +4941,23 @@ "integrity": "sha1-l9loeCgbFLUyy84QF4Xf0cs0CWQ=" }, "gl-volume3d": { - "version": "git+https://github.com/gl-vis/gl-volume3d.git#c222d11c950230e1882b3072673924474090f856", - "requires": { - "barycentric": "1.0.1", - "colormap": "2.3.0", - "gl-buffer": "2.1.2", - "gl-mat4": "1.2.0", - "gl-shader": "4.2.1", - "gl-texture2d": "2.1.0", - "gl-vao": "1.3.0", - "glsl-specular-cook-torrance": "2.0.1", - "glslify": "6.1.1", - "ndarray": "1.0.18", - "normals": "1.1.0", - "polytope-closest-point": "1.0.0", - "simplicial-complex-contour": "1.0.2", - "typedarray-pool": "1.1.0" + "version": "git://github.com/gl-vis/gl-volume3d.git#6e28d3d432f67c4da968e68d5ff474f4c49b4412", + "from": "git://github.com/gl-vis/gl-volume3d.git#6e28d3d432f67c4da968e68d5ff474f4c49b4412", + "requires": { + "barycentric": "^1.0.1", + "colormap": "^2.1.0", + "gl-buffer": "^2.0.8", + "gl-mat4": "^1.0.0", + "gl-shader": "^4.2.1", + "gl-texture2d": "^2.0.8", + "gl-vao": "^1.1.3", + "glsl-specular-cook-torrance": "^2.0.1", + "glslify": "^6.1.1", + "ndarray": "^1.0.15", + "normals": "^1.0.1", + "polytope-closest-point": "^1.0.0", + "simplicial-complex-contour": "^1.0.0", + "typedarray-pool": "^1.1.0" } }, "glob": { diff --git a/package.json b/package.json index aabcc8cbeb2..f81360c9b9d 100644 --- a/package.json +++ b/package.json @@ -86,7 +86,7 @@ "gl-streamtube3d": "^1.1.0", "gl-surface3d": "^1.3.6", "gl-text": "^1.1.6", - "gl-volume3d": "^0.5.0", + "gl-volume3d": "git://github.com/gl-vis/gl-volume3d#6e28d3d432f67c4da968e68d5ff474f4c49b4412", "glslify": "^6.3.1", "has-hover": "^1.0.1", "has-passive-events": "^1.0.0",