-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
3D cone traces #2641
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
3D cone traces #2641
Changes from 7 commits
ec64895
5736290
cb7cb7f
89eaf11
863b0da
fa32a74
71e46af
a2c3694
a2db567
db8222b
55700b8
e4a2035
a02dd11
cb7ef43
e718c66
f651eec
3f3bfac
aaf7249
d5d6f33
b7465fb
5a42de0
04d3d91
2a45dae
8dca9ae
3d26d47
5a59bc7
3dd6cf5
fb2ec1e
74ecbf8
abb11e0
8e8f5d6
2c08357
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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/cone'); |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -70,6 +70,7 @@ | |
"es6-promise": "^3.0.2", | ||
"fast-isnumeric": "^1.1.1", | ||
"font-atlas-sdf": "^1.3.3", | ||
"gl-cone3d": "[email protected]:gl-vis/gl-cone3d#c675b25e5991e63d0ad23ed24d712c496c28cd72", | ||
"gl-contour2d": "^1.1.4", | ||
"gl-error3d": "^1.0.7", | ||
"gl-heatmap2d": "^1.0.4", | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
/** | ||
* 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 positions of the cones.', | ||
'When `vx`, `vy`, `vz` are not set,', | ||
' these are also the x coordinates of the u/v/w vector field.' | ||
].join(' ') | ||
}, | ||
y: { | ||
valType: 'data_array', | ||
role: 'info', | ||
editType: 'calc+clearAxisTypes', | ||
description: [ | ||
'Sets the y positions of the cones.', | ||
'When `vx`, `vy`, `vz` are not set,', | ||
' these are also the y coordinates of the u/v/w vector field.' | ||
].join(' ') | ||
}, | ||
z: { | ||
valType: 'data_array', | ||
role: 'info', | ||
editType: 'calc+clearAxisTypes', | ||
description: [ | ||
'Sets the z positions of the cones.', | ||
'When `vx`, `vy`, `vz` are not set,', | ||
' these are also the z coordinates of the u/v/w vector field.' | ||
].join(' ') | ||
}, | ||
|
||
u: { | ||
valType: 'data_array', | ||
editType: 'calc', | ||
description: 'Sets the x components of the vector field.' | ||
}, | ||
v: { | ||
valType: 'data_array', | ||
editType: 'calc', | ||
description: 'Sets the y components of the vector field.' | ||
}, | ||
w: { | ||
valType: 'data_array', | ||
editType: 'calc', | ||
description: 'Sets the z components of the vector field.' | ||
}, | ||
|
||
vx: { | ||
valType: 'data_array', | ||
editType: 'calc', | ||
description: 'Sets the x coordinates of the vector field mesh.' | ||
}, | ||
vy: { | ||
valType: 'data_array', | ||
editType: 'calc', | ||
description: 'Sets the y coordinates of the vector field mesh.' | ||
}, | ||
vz: { | ||
valType: 'data_array', | ||
editType: 'calc', | ||
description: 'Sets the z coordinates of the vector field mesh.' | ||
}, | ||
|
||
sizemode: { | ||
valType: 'enumerated', | ||
values: ['scaled', 'absolute'], | ||
role: 'info', | ||
editType: 'calc', | ||
dflt: 'scaled', | ||
description: [ | ||
'Sets the mode by which the cones are sized.', | ||
'If *scaled*, `sizeref` scales such that the reference cone size', | ||
'for the maximum vector magnitude is 1.', | ||
'If *absolute*, `sizeref` scales such that the reference cone size', | ||
'for vector magnitude 1 is one grid unit.' | ||
].join(' ') | ||
}, | ||
sizeref: { | ||
valType: 'number', | ||
role: 'info', | ||
editType: 'calc', | ||
min: 0, | ||
dflt: 1, | ||
description: 'Sets the cone size reference value.' | ||
}, | ||
|
||
text: { | ||
valType: 'string', | ||
role: 'info', | ||
dflt: '', | ||
arrayOk: true, | ||
editType: 'calc', | ||
description: [ | ||
'Sets the text elements associated with the cones.', | ||
'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', 'flatshading', 'lightposition', 'lighting']; | ||
|
||
fromMesh3d.forEach(function(k) { | ||
attrs[k] = mesh3dAttrs[k]; | ||
}); | ||
|
||
attrs.hoverinfo = extendFlat({}, baseAttrs.hoverinfo, {editType: 'calc'}); | ||
|
||
module.exports = attrs; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
/** | ||
* 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 cone2mesh = require('./helpers').cone2mesh; | ||
var colorscaleCalc = require('../../components/colorscale/calc'); | ||
|
||
module.exports = function calc(gd, trace) { | ||
var fullLayout = gd._fullLayout; | ||
|
||
// TODO skip when 'cmin' and 'cmax' are set | ||
// TODO find way to "merge" this cone2mesh call with the one in convert.js | ||
// | ||
// TODO should show in absolute or normalize length? | ||
|
||
var meshData = cone2mesh(trace, fullLayout[trace.scene]); | ||
|
||
colorscaleCalc(trace, meshData.vertexIntensity, '', 'c'); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
/** | ||
* 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 Plots = require('../../plots/plots'); | ||
var Colorscale = require('../../components/colorscale'); | ||
var drawColorbar = require('../../components/colorbar/draw'); | ||
|
||
module.exports = function colorbar(gd, cd) { | ||
var trace = cd[0].trace; | ||
var cbId = 'cb' + trace.uid; | ||
var cmin = trace.cmin; | ||
var cmax = trace.cmax; | ||
|
||
gd._fullLayout._infolayer.selectAll('.' + cbId).remove(); | ||
|
||
if(!trace.showscale) { | ||
Plots.autoMargin(gd, cbId); | ||
return; | ||
} | ||
|
||
var cb = cd[0].t.cb = drawColorbar(gd, cbId); | ||
var sclFunc = Colorscale.makeColorScaleFunc( | ||
Colorscale.extractScale(trace.colorscale, cmin, cmax), | ||
{noNumericCheck: true} | ||
); | ||
|
||
cb.fillcolor(sclFunc) | ||
.filllevels({start: cmin, end: cmax, size: (cmax - cmin) / 254}) | ||
.options(trace.colorbar)(); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
/** | ||
* 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 createScatterPlot = require('gl-scatter3d'); | ||
var createConeMesh = require('gl-cone3d').createConeMesh; | ||
var cone2mesh = require('./helpers').cone2mesh; | ||
|
||
function Cone(scene, uid) { | ||
this.scene = scene; | ||
this.uid = uid; | ||
this.mesh = null; | ||
this.pts = null; | ||
this.data = null; | ||
} | ||
|
||
var proto = Cone.prototype; | ||
|
||
proto.handlePick = function(selection) { | ||
if(selection.object === this.pts) { | ||
var selectIndex = selection.index = selection.data.index; | ||
|
||
selection.traceCoordinate = [ | ||
this.data.x[selectIndex], | ||
this.data.y[selectIndex], | ||
this.data.z[selectIndex] | ||
]; | ||
|
||
var text = this.data.text; | ||
if(Array.isArray(text) && text[selectIndex] !== undefined) { | ||
selection.textLabel = text[selectIndex]; | ||
} else if(text) { | ||
selection.textLabel = text; | ||
} | ||
|
||
return true; | ||
} | ||
}; | ||
|
||
function convert(scene, trace) { | ||
return cone2mesh(trace, scene.fullSceneLayout, scene.dataScale); | ||
} | ||
|
||
proto.update = function(data) { | ||
this.data = data; | ||
|
||
var meshData = convert(this.scene, data); | ||
|
||
this.mesh.update(meshData); | ||
this.pts.update({position: meshData._pts}); | ||
}; | ||
|
||
proto.dispose = function() { | ||
this.scene.glplot.remove(this.pts); | ||
this.pts.dispose(); | ||
this.scene.glplot.remove(this.mesh); | ||
this.mesh.dispose(); | ||
}; | ||
|
||
function createConeTrace(scene, data) { | ||
var gl = scene.glplot.gl; | ||
|
||
var meshData = convert(scene, data); | ||
var mesh = createConeMesh(gl, meshData); | ||
|
||
var pts = createScatterPlot({ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice, that was fast! Is it possible to get There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I like it! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done in e4a2035 |
||
gl: gl, | ||
position: meshData._pts, | ||
project: false, | ||
opacity: 0 | ||
}); | ||
|
||
var cone = new Cone(scene, data.uid); | ||
cone.mesh = mesh; | ||
cone.pts = pts; | ||
cone.data = data; | ||
mesh._trace = cone; | ||
pts._trace = cone; | ||
|
||
scene.glplot.add(pts); | ||
scene.glplot.add(mesh); | ||
|
||
return cone; | ||
} | ||
|
||
module.exports = createConeTrace; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you mean by a grid unit here? Is it one unit of the
(x, y, z)
positions or the spacing between adjacent cones?Almost always the vectors have different units from the position space they're displayed in - so I'd think if you specify nothing at all about the cone sizing, they should end up scaled so the largest one is about as big as the smallest spacing between adjacent cones.
There's definitely a use case for
'absolute'
- the user has pre-calculated everything and they want each cone to go from one precise(x, y, z)
to another precise(x, y, z)
. I'm not sure there's a use case though for scaling to one unit in position, I'd think'scaled'
should refer to the minimum adjacent cone distance.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be honest, I'm not sure. I copied this from https://github.com/gl-vis/gl-cone3d#constructor I'll have to inspect
https://github.com/gl-vis/gl-cone3d/blob/c675b25e5991e63d0ad23ed24d712c496c28cd72/cone.js#L138-L246
a little bit more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll have a new look at the cone sizing parameters. Pre-meshgrid they were based on the position space units and scaled so that two cones with maximum norm, pointing to the same direction, would have their tip and base touch. Like <|<|. That would be with coneSize 2. With coneSize 1, you'd get no overlapping cones even when they're |><|.
The absolute sizing ditched the automatic cone scaling and used the cone vector norm to figure out the size of the cone model, so that a norm of 1 with absolute size factor 1 results in a cone that's one unit tall in the position unit space. Absolute size factor 2 => 2 unit tall cone, etc.
With the meshgrid sampling (and the realization that the cone positions may not be on a nice regular grid), I changed the automatic cone sizing to scale based on the smallest distance between two adjacent cone positions (adjacent in the sense that they've got successive indices in the positions array). This is not the right thing to do though, doing a smallest distance between any two cone positions would be, but that's O(n^2) with a naive algo.
Another idea floated on the sizing was to have pixel-based sizing as well, which'd probably work like "invert the projection, view and model transform matrices for cone model, scale cone model according to viewport size" but would result in ortho camera cones which might be confusing. (And it sounds like a bit of a pain to implement.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kig that all sounds great. I guess we only need to worry about the smallest-distance calculation when using the
(x, y, z)
data to position the cones; With the cartesian product of x/y/z interpolation grids each dimension should be pretty compact, even if the values are out of order. But I guarantee at some point we'll see a data set where the closest points are not successive. I don't know if there's a JS implementation already available but it's possible to do it in O(n log(n)^2) http://www.cs.ucsb.edu/~suri/cs235/ClosestPair.pdfLets not worry about pixel-based sizing, at least not for now. I guess I can see using this if we also do automatic grid spacing, ie when you zoom in and the cone spacing gets larger, at some point we decrease the grid size so we draw more cones between the existing ones. That's definitely not a v1 feature 😅