-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
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
On-par autorange for scattergl #2404
Conversation
... replot is sufficient since regl push.
... to make things look a little more like the rest of plotly.js
- reuse scatter axis-expansion logic - improve 'fast' axis expand routine (using average marker.size as pad value) - use ax.makeCalcdata for all axis types (this creates a new array for linear axes, but makes thing more robust) - add a few TODOs
- most notable change is in gl2d_axes_label2 that now shows the correct to-zero autorange.
src/traces/scattergl/index.js
Outdated
// regl-scatter2d uses NaNs for bad/missing values | ||
// | ||
// TODO should this be a Float32Array ?? | ||
var positions = new Array(count2); |
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.
@dfcreative could we set up positions
with a Float32Array
here?
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.
Float32 is not enough precision for some timedate and precise linear/etc ranges.
|
||
var x = xaxis.type === 'linear' ? trace.x : xaxis.makeCalcdata(trace, 'x'); | ||
var y = yaxis.type === 'linear' ? trace.y : yaxis.makeCalcdata(trace, 'y'); |
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.
IMPORTANT with typed array support in mind (-> #2388), I made all traces pass through makeCalcdata
unlike previously where x/y array on linear axes bypassed it. Please note that makeCalcdata
creates a new array (i.e. x
isn't the same as trace.x
), unless trace.x
is a typed array. So memory-conscious user should switch to using typed arrays.
Note that at 1e6
on my setup, one makeCalcdata
call clocks in at roughly 25ms
. So using typed arrays can save about 50ms
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.
Even with the extra arrays, memory consumption appears fine. I wouldn't having @dfcreative double-check though.
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 guess there are situations we accept now that would have broken previously (but only on linear axes) - the "junk" characters stripped by cleanNumber
- worth a test case?
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.
Yes. Good call!
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.
For reference, here's a few benchmarks I took down while working on this PR: At 1e6 points:
At 1e5-1 points:
A few more notable benchmarks:
I used Chrome 64 on a Lenovo t450s with Ubuntu 16.04. |
src/traces/scattergl/index.js
Outdated
x[i] = i; | ||
if(xa.type === 'log') { | ||
for(i = 0; i < count2; i += 2) { | ||
positions[i] = xa.d2l(positions[i]); |
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.
🐎 we've already been through makeCalcData
so we should be able to do c2l
here
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.
Thanks for pointing this out! Done in -> 98d2407
calcAxisExpansion(gd, trace, xa, ya, x, y, ppad); | ||
} else { | ||
if(markerOptions) { | ||
ppad = 2 * (markerOptions.sizeAvg || Math.max(markerOptions.size, 3)); |
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.
How did you find sizeAvg
to work in practice? A little hard to say I guess, since we don't have a lot of real data > 1e5 points to play with... You're right that we don't want to just use sizeMax
, it's worth accepting a bit of clipping in order to generally have less wasted space, and much of the time the largest point won't be on any edge... just wondering how that balance plays out in practice, whether we would be better off with something like halfway between the average and max.
Anyway perhaps we don't have a god way to answer that question right now. I'll just mention that in case we do want to try and do better later, we could find some heuristics that only add a little bit of computation, like binning data points into top/middle/bottom thirds, and only using the top third for the top padding... maybe even with a smooth weighting of the size based on how far it is from the edge. That would still be far faster than the full calculation but could do a better job reducing wasted space without too much clipping.
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.
-> #2417
}) | ||
.then(function() { | ||
expect(gd._fullLayout.xaxis.range).toBeCloseToArray(glRangeX, 'x range'); | ||
expect(gd._fullLayout.yaxis.range).toBeCloseToArray(glRangeY, 'y range'); |
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.
Beautiful test!
|
||
// prepare colors | ||
if(multiMarker || Array.isArray(markerOpts.color) || Array.isArray(markerOpts.line.color) || Array.isArray(markerOpts.line) || Array.isArray(markerOpts.opacity)) { | ||
if(multiSymbol || multiColor || multiLineColor || multiOpacity) { |
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.
<3
{name: 'annotations', fig: require('@mocks/gl2d_annotations.json')} | ||
]; | ||
|
||
specs.forEach(function(s) { |
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.
Lovely!
@dfcreative @alexcjohnson all comments have been addressed. |
LGTM! 💃 |
💃 from me too! 🎉 |
fixes #2354 - to be merged into #2388
This gets
scattergl
autorange to parity withscatter
by reusing its calc-step logic straight-up for data arrays of length less than1e5
. I made a few more improvements toScattergl.calc
that will be highlighted in 0aa0f5e.I also made a linting commit 5316c47 mostly renaming
container
->gd
makingscattergl/index.js
look a little more like the rest of our codebase. I hope @dfcreative won't mind.